react-router-dom
react-router-dom
。对于每一个 React Router 应用来说,都应该有一个路由器组件,它们会为应用创建一个专用的 history
对象。
针对 Web 项目,react-router-dom
提供了 <BrowserRouter>
和 <HashRouter>
。
主要区别:
从原理上:
从用法上:
其实就是路由的hash和history两种模式,并且这两个组件是路由的容器,必须在最外层。
<HashRouter>
...
</HashRouter>
<BrowserRouter>
...
</BrowserRouter>
路由匹配是通过比较 <Route>
组件的 path
属性和当前地址的 pathname 实现的
如果匹配则渲染当前路由组件的内容,如果不匹配则渲染 null。
注意:没有提供 path
属性的 <Route>
组件将总是匹配。
import Profile from './Profile';
// v5
<Route path=":userId" component={Profile} />
<Route
path=":userId"
render={routeProps => (
<Profile routeProps={routeProps} animate={true} />
)}
/>
// v6
<Route path=":userId" element={<Profile />} />
<Route path=":userId" element={<Profile animate={true} />} />
<Route path=":userId" element={<Profile animate={true} /> />
function Profile({ animate }) {
const params = useParams();
const location = useLocation();
}
通过这种形式:
1. 可以向组件传 props,如上面的 animate={true}
2. 因为有了 hook 的出现,所以不必再通过 renderProps 向组件传递路由的一些 props,我们可以通过useParams、useLocation 就可以拿到这些信息
<BrowserRouter>
<Routes>
<Route path="/" element={<Home />} />
<Route path="/users" element={<Users />}>
<Route path="me" element={<OwnUserProfile />} />
<Route path=":id" element={<UserProfile />} />
</Route>
</Routes>
</BrowserRouter>
那么我们要显示 OwnUserProfile 组件的话通过/users/me 就可以定位到了,这看起来相当的直观
v6 简化了 path 的格式,只支持两种动态占位符。
正确的格式:
path = '/groups'
path = '/groups/admin'
path = '/users/:id'
path = '/users/:id/messages'
path = '/files/*' // 通配符放在末尾
path = '/files/:id/*'
path = '/files-*'
错误的格式:
path = '/users/:id?' // ? 不满足上面两种格式
path = '/tweets/:id(\d+)' // 有正则,不满足上面两种格式
path = '/files/*/cat.jpg'// 通配符不能放中间
index
即表示是否是主路由,如果设置为 true 的话不能有 children
<Routes>
<Route element={<Layout />}>
<Route index element={<Home />} />
<Route path="about" element={<About />}/>
...
</Route>
</Routes>
<Outlet/>
的作用类似插槽,用于匹配子路由的 element
function App() {
return (
<div>
<BrowserRouter>
<Routes>
<Route exact path={'/'} element={<Login />}/>
<Route path={'/login'} element={<Login />}/>
匹配到/index里面开始渲染<PrivateRoute/> 组件
<PrivateRoute/> 返回<Outlet>相当于 开始渲染当前路由的子路由
<Route path={'/index'} element={<PrivateRoute/>}>
即开始渲染<Layoutdemo/>组件
<Route path={'/index'} element={<Layoutdemo/>}>
<Route path={'/index/goods'} element={<Goods/>}/>
<Route path={'/index/addgoods'} element={<AddGoods/>}/>
<Route path={'/index/category'} element={<Category/>}/>
</Route>
</Route>
</Routes>
</BrowserRouter>
</div>
);
}
let jian = () =>{ //判断用户登录信息是否存在token 存在返回true 跳进主页面
const token = sessionStorage.getItem('token');
return token ? true : false;
}
const PrivateRoute = () => {
return jian() ? <Outlet/> : <Navigate to={'/login'}/>
}
所有路由的定义放在最外边的组件里面 层次清楚
上面我们都是把 Route 作为 Routes 的 children
function App() {
return (
<Routes>
<Route path='/' element={<Layout />}>
<Route path='auth/*' element={<Auth/> } />
<Route path='basic/*' element={<Basic/> } />
</Route>
</Routes>
)
}
我们还可以通过useRoutes
生成对应的 element 这种配置项让我们可以清晰地看出路由的嵌套结构
import { useRoutes } from 'react-router-dom'
function App() {
const element = useRoutes([
{
path: '/',
element: <Layout />,
children: [
{
path: 'auth/*',
element: <Auth/>
},
{
path: 'basic/*',
element: <Basic/>
}
]
}
])
return (
{element}
)
}
该顶级组件将被重命名。但是,其功能大部分保持不变。
<Switch>
:
<Switch>
组件用于给 <Route>
组件分组,可以将一组 <Route>
组件嵌套在 <Switch>
组件中。<Route>
组件通过比较它的 path
属性和当前地址来判断是否渲染它的内容,所以就会存在多个 <Route>
匹配成功且都渲染它们内容的情况。为了避免这样的情况,<Switch>
组件就起了作用,它会迭代当中的 <Route>
组件,并选出第一个匹配的 <Route>
来渲染// v5
<Switch>
<Route exact path='/' component={Home} />
<Route path='/about' component={About} />
<Route path='/contact' component={Contact} />
{/* when none of the above match, <NoMatch> will be rendered */}
<Route component={NoMatch} />
</Switch>
<Switch>
<Route exact path="/"><Home /></Route>
<Route path="/profile"><Profile /></Route>
</Switch>
// v6
<Routes>
<Route path="/" element={<Home />} />
<Route path="profile/*" element={<Profile />} />
</Routes>
两者都是跳转路由,NavLink的参数更多些。
在v5中,如果 to
不以 /
开头的话会让人有点迷,因为这取决于当前的 URL。
比如当前 URL 是/user
, 那么<Link to="me">
会渲染成 <a href="/me">
如果是 /users/
,那么又会渲染成 <a href="/users/me">
在 v6
中,无论当前 URL 是/user
还是/users/
, <Link to="me">
都会渲染成<a href='/user/me'>
。
也就是说 to
更像我们常用的 cd
命令行
<Route path="app">
<Route path="dashboard">
<Route path="stats" />
</Route>
</Route>
// 当前 URL 为 /app/dashboard 或 /app/dashboard/
<Link to="stats"> => <a href="/app/dashboard/stats">
<Link to="../stats"> => <a href="/app/stats">
<Link to="../../stats"> => <a href="/stats">
<Link to="../../../stats"> => <a href="/stats">
// 命令行中, 当前目录为 /app/dashboard
cd stats # pwd is /app/dashboard/stats
cd ../stats # pwd is /app/stats
cd ../../stats # pwd is /stats
cd ../../../stats # pwd is /stats
replace:boolean ---->默认 false,即跳转路由要用 push
还是 replace
import { useNavigate } from "react-router-dom";
function App() {
const navigate = useNavigate();
function handleClick() {
navigate("/home");
}
return (
<div>
<button onClick={handleClick}>go home</button>
</div>
);
}
// v6
navigate('/home');
//v5
history.push('/home')
// v6
navigate('/home', { replace: true });
//v5
history.replace('/home')
// v6
import { useNavigate } from "react-router-dom";
function App() {
const navigate = useNavigate();
return (
<>
<button onClick={() => navigate(-2)}>
Go 2 pages back
</button>
<button onClick={() => navigate(-1)}>Go back</button>
<button onClick={() => navigate(1)}>
Go forward
</button>
<button onClick={() => navigate(2)}>
Go 2 pages forward
</button>
</>
);
}
// v5
import { useHistory } from "react-router-dom";
function App() {
const { go, goBack, goForward } = useHistory();
return (
<>
<button onClick={() => go(-2)}>
Go 2 pages back
</button>
<button onClick={goBack}>Go back</button>
<button onClick={goForward}>Go forward</button>
<button onClick={() => go(2)}>
Go 2 pages forward
</button>
</>
);
}
<Route path="app">
<Route path="dashboard">
<Route path="stats" />
</Route>
</Route>
// 当前 URL 为 /app/dashboard 或 /app/dashboard/
<Link to="stats"> => <a href="/app/dashboard/stats">
<Link to="../stats"> => <a href="/app/stats">
<Link to="../../stats"> => <a href="/stats">
<Link to="../../../stats"> => <a href="/stats">
// 当前 URL 为 /app/dashboard 或 /app/dashboard/
const navigate = useNavigate()
navigate('stats') => '/app/dashboard/stats'
navigate('../stats') => '/app/stats'
navigate('../../stats') => '/stats'
navigate('../../../stats') => '/stats'
// 命令行中, 当前目录为 /app/dashboard
cd stats # pwd is /app/dashboard/stats
cd ../stats # pwd is /app/stats
cd ../../stats # pwd is /stats
cd ../../../stats # pwd is /stats