前言
之前一直负责老项目的迭代,而且很少关注router相关的代码,直到上个月公司开了一个新项目,当我着手开始配置路由时突然发现,嗯?Switch标签咋没了?Route里的component属性咋也没了。意识到不妙的我赶紧去翻了翻react router的文档,发现react router早就在21年底就偷偷升级到了v6且变更极大(嗯 这是一个悲伤的故事,现在甚至已经更新到了6.4.1)
之前的老项目的包版本是5.2的,要升级的话成本有点高,也懒得搞,就那样吧。但新项目肯定不能将就啊。那就整呗
正文
首先先说一下react-router和react-router-dom的区别,react-router实现了路由的核心功能,react-router-dom则是基于react-router,又加了在浏览器运行环境下的一些功能,比如把Link组件渲染成一个a标签,再比如HashRouter会使用window.location.hash和hashChange事件构建路由。一般在项目中只需要引入react-router-dom包即可。
npm install react-router-dom
BrowserRouter
新项目使用浏览器路由即BrowserRouter,要想在项目中使用React Router,需要App组件嵌套进BrowserRouter组件中。
ReactDOM.createRoot(document.getElementById("root") as HTMLElement).render(
<React.StrictMode>
<BrowserRouter>
<App />
</BrowserRouter>
</React.StrictMode>
);
Routes和Route
我们使用Route组件来定义路由,并使用Routes包裹。
Route组件接收两个props:
- path?: string | 页面url
- element?: React.ReactNode |当前url需要加载的元素
<Routes>
<Route path="/" element={<Home />} />
<Route path="/teams/:id" element={<Teams />} />
<Route path="/teams/new" element={<NewTeams />} />
<Route path="*" element={<PageError />} />
</Routes>
<Switch>
<Route path="/teams/:id" component={Teams} />
<Route path="/teams/new" component={NewTeams} />
</Switch>
V6的Route组件不在要求我们按一定的顺序来定义路由,在旧版本中,当多个路由匹配一个模糊的url时,我们必须以某种方式对路由进行排序,v6版本则会替我们选择最具体的匹配。/teams/new将匹配这两个路由,但只会渲染NewTeams组件。
支持的格式:
- /groups
- /groups/admin
- /users/:id
- /users/:id/messages
- /files/*
- /files/:id/*
嵌套路由
react-router V6支持路由的嵌套,允许父路由控制子路由的渲染,
提供了一个渲染出口组件 Outlet,当匹配到子路由的时候会渲染在Outlet组件所在的位置,父路由此时仍然存在。
支持多级嵌套
const src = () => {
return (
<>
<Routes>
<Route path="/" element={<Home />} />
<Route path="/list" element={<List />}>
<Route path="Detail/:id" element={<Detail />} />
</Route>
</Routes>
</>
)
}
const List = () => {
const navigate = useNavigate();
return (
<>
<div className="list">
<Button
onClick={() => {
navigate('/list/detail/1');
}}
>
detail1
</Button>
<Button
onClick={() => {
navigate('/list/detail/2');
}}
>
detail2
</Button>
// react-router提供了Outlet组件用来占位置
<Outlet />
</div>
</>
);
查询参数
/mysql?mysql_group_id=mysql-test&mysql_instance_id=mysql-asfasfew
react-router V6提供了useSearchParams Hook,它是基于浏览器内置的 URLSearchParams 构造函数进行的封装。
useSearchParams类似useState,返回一个数组,第一个元素是一个Map,可以通过其get方法获取查询字符串中的值,第二个元素是一个函数,用来更新url中的查询字符串。
import { useSearchParams } from 'react-router-dom';
const [searchParams, setSearchParams] = useSearchParams();
const groupId = searchParams.get('mysql_group_id')
const instanceId = searchParams.get('mysql_instance_id')
const updateOrder = (sort) => {
setSearchParams({ key: value })
}
编程式配置路由
react-router V6还提供了 useRoutes Hook,使我们可以通过写一个配置对象的方式来配置路由,不需要在自行拼接jsx.
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-u9plstJr-1665650846994)(https://p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/a9c3dd7664664973b0661b29800bb814~tplv-k3u1fbpfcp-zoom-1.image)]
const routerConfig = [
{
path: "/",
key: "home",
label: "common.nav.menu.home",
icon: <HomeOutlined />,
element: <Home />,
},
{
key: "authManageWrapper",
label: "common.nav.menu.authManage",
icon: <SolutionOutlined />,
children: [
{
path: "/auth/list",
key: "authList",
label: "common.nav.menu.authList",
element: <AuthList />,
},
{
path: "/auth/add",
key: "addAuth",
label: "common.nav.menu.addAuth",
element: <AddAuth />,
},
],
},
{
path: "*",
hideInMenu: true,
key: "null",
element: <Navigate to="/" />,
},
]
return (
<>
{useRoutes(routerConfig)}
</>
)
在antd的4.20版本+中,他的Menu导航组件也支持这种写法,可以维护一份routerConfig,对其扩充一些属性,就能同时再Menu组件中使用。
其他
- 新增useNavigate Hook 代替useHistory Hook
let navigate = useNavigate();
<button
onClick={() => {
navigate("/invoices" + location.search);
}}
>
jump
</button>
- 可以在各个地方渲染多组路由:经常有一些需求让我们在某个state为true时展示组件A,为false时展示组件B,这个时候可以尝试在组件内写一个嵌套路由。
总结:
- 定义路由时 Routes组件和Route组件搭配使用,且可以在不同地方定义多组。Route组件的属性也发生了变更。
- 新增了Outlet组件,支持路由的嵌套,使用起来可以更加的灵活。
- 新增了一个可以查询和更新url参数的hook useSearchParams。
- 提供了 useRoutes Hook, 支持使用js对象的形式来定义路由。