# React-router-v6

By [zksyncV2](https://paragraph.com/@zksyncv2) · 2021-12-21

---

> TODO 内部分享文字稿，有时间需要斟酌文笔以及要点准确性

> 原文链接
> 
> ([https://github.com/remix-run/react-router/blob/main/docs/upgrading/v5.md)\[https://github.com/remix-run/react-router/blob/main/docs/upgrading/v5.md\]](https://github.com/remix-run/react-router/blob/main/docs/upgrading/v5.md)%5B[https://github.com/remix-run/react-router/blob/main/docs/upgrading/v5.md%5D](https://github.com/remix-run/react-router/blob/main/docs/upgrading/v5.md%5D))

新特性
---

注：本文带有部分我的主观意识，删除了一些自认为不重要的内容，详细推荐阅读原文。

按照惯例提前总结全文关键内容，感兴趣可以下滑详细阅读。

升级的前提：

1.  router 团队建议先升级到 5.1 版本并使用新的 API 来渐进式升级到 v6
    
2.  不再兼容 react v16.8 以下版本
    

主要改动内容：

1.  废弃 `Switch` 组件，由 `Routes` 代替（使用了智能匹配路径算法）
    
2.  废弃 `Redirect` 组件，由 `Navigate` 代替
    
3.  废弃 `useHistory` 方法，由 `useNavigate` 代替
    
4.  `Route` 组件移除原有 `component` 及 `render` 属性，统一通过 `element` 属性传递：`<Route element={<Home />}>`
    
5.  `Route` 组件支持嵌套写法（v3 版本用法回归）
    
6.  `Route` 组件的 `path` 规则变更，不再支持类正则写法
    
7.  消除了 `v5` 版本中带后斜杠的路径时，`Link` 组件的跳转模糊的问题
    
8.  `Link` 组件支持自动携带当前父路由，以及相对路径写法`../home`
    
9.  新增 `useRoutes` 方法，代替之前的`react-router-config`写法，同样支持嵌套
    
10.  其他一些 API 名称变更
    

开始升级
----

### 升级 react 版本至 16.8 以上

> React Router v6 makes heavy use of React hooks, so you'll need to be on React 16.8 or greater before attempting the upgrade to React Router v6. The good news is that React Router v5 is compatible with React >= 15, so if you're on v5 (or v4) you should be able to upgrade React without touching any of your router code.

新版本大量使用 hooks，所以必须要安装 16.8 版本以上的 react。

目前大多数 react 项目都已经使用 17 版本了，没什么可说的。至于巨石项目，还是暂时先死了这条心...

### 升级 react-router-dom 至 5.1

> It will be easier to make the switch to React Router v6 if you upgrade to v5.1 first. In v5.1, we released an enhancement to the handling of elements that will help smooth the transition to v6. Instead of using and props, just use regular element everywhere and use hooks to access the router's internal state.

react router 团队建议先升级到 v5.1 版本，并采用一些新的 API，有助于渐进式升级到 v6。

不过我都想要升级了，还在乎那一哆嗦吗（主要还是为了巨石..）

*   在 5.1 版本之前的使用方法
    

    import { Switch, Route } from 'react-router-dom';
    
    const User = ({ id }: { id: string }) => {
      return <div>user id: {id}</div>;
    };
    
    const App = () => {
      return (
        <Switch>
          <Route exact path="/" component={Home} />
          <Route path="/user/:id" render={(route) => <User id={route.match.params.id} />} />
        </Switch>
      );
    };
    

*   5.1 推荐用法
    

    import { Switch, Route, useParams } from 'react-router-dom';
    
    const User = () => {
      const { id } = useParams<{ id: string }>();
      return <div>user id: {id}</div>;
    };
    
    const App = () => {
      return (
        <Switch>
          <Route exact path="/">
            <Home />
          </Route>
          {/* 同时支持props.children属性传递 */}
          <Route path="/user/:id" children={<User />} />
        </Switch>
      );
    };
    

[React Router hooks 使用文档及基本原理](https://reacttraining.com/blog/react-router-v5-1/)

> In general, React Router v5.1 (and v6) favors elements over components (or "element types"). There are a few reasons for this, but we'll discuss more further down when we discuss v6's API.

5.1 版本之后，react router 更加倾向于 ReactElement 类型而不是 ReactComponent 类型，在后面的文档会详细介绍

> When you use regular React elements you get to pass the props explicitly. This helps with code readability and maintenance over time. If you were using to get a hold of the params, you can just useParams inside your route component instead.

`useParams()`比`<Route render={(route) => <User id={route.match.params.id} />}>`可读性以及维护性更好。

> Along with the upgrade to v5.1, you should replace any usage of withRouter with hooks. You should also get rid of any "floating" elements that are not inside a . Again, the blog post about v5.1 explains how to do this in greater detail.

[5.1 版本发布文档](https://reacttraining.com/blog/react-router-v5-1/)

在 5.1 版本中，应该使用 hooks 代替 withRouter，也应该移除掉在 Switch 外部的 Route 节点。

总之，升级到 5.1 之后需要做以下几件事情：

*   使用`<Route children>`替换`<Route render>`或`<Route children>`
    
*   使用`hooks`获取当前 router 信息，例如`useLocation()`或`useParams()`
    
*   移除`withRouter`装饰器写法
    
*   将在`<Switch>`外部的`<Route>`统一替换为`useRouteMatch`或将他们挪到`<Switch>`中
    

#### 移除 Switch 内部的 Redirect 节点

> If you want to redirect on the initial render, you should move the redirect logic to your server (we wrote more about this here).

如果需要初始化渲染的时候就执行重定向，推荐将重定向逻辑放在服务端实现「[详细说明](https://gist.github.com/mjackson/b5748add2795ce7448a366ae8f8ae3bb)」。

文章中描述了几点初始化时重定向的弊端，主要如下：

*   客户端并不是真正的重定向，此时的 HTTP 状态码是 200。爬虫读取页面时并不会执行客户端的重定向方法，不利于 SEO
    
*   在 SSR 时，执行`ReactDOMServer.renderToString()`得到的结果是一次重定向，浪费时间和资源
    

> If you want to redirect client-side, move your into a prop.

客户端执行重定向推荐使用 Route 的 render 属性来渲染 Redirect

    // 5.1 之前
    <Switch>
      <Redirect from="about" to="about-us" />
    </Switch>
    
    // 5.1
    <Switch>
      <Route path="about" render={() => <Redirect to="about-us" />} />
    </Switch>
    

> Normal elements that are not inside a are ok to remain. They will become elements in v6.

这是第一个破坏性变更：在 v6 版本中废弃`Redirect`，由`Navigate`代替。

#### 重写你的「自定义 Route」

> Replace any elements inside a that are not plain elements with a regular . This includes any -style custom components.

应该很少会有自定义 Route 的场景出现，这段先暂时不关心。

* * *

### 升级到 v6 版本

#### 使用 Routes 替换 Switch

> React Router v6 introduces a Routes component that is kind of like Switch, but a lot more powerful.

v6 版本提供了功能更强大的`Routes`组件来代替`Switch`，主要优点如下：

算了...原文的几个优点基本都很少用过。比如示例中的嵌套路由，在实际项目中一般直接写好 config 文件通过 map 渲染，不过`Routes`不再按顺序匹配路径，而是采用了一种自动匹配最佳路径的方法，这块之后有时间可以阅读下源码或等等大佬们的源码分析贴。

对优点和示例感兴趣的话可以直接查看[原文](https://github.com/remix-run/react-router/blob/main/docs/upgrading/v5.md#upgrade-all-switch-elements-to-routes)

这算是文章中的第二个破坏性变更：废弃`Switch`，由`Routes`代替

#### 的优势 For starters, we see React itself taking the lead here with the API. The fallback prop takes a React element, not a component. 在 React 的 Suspense 组件中，fallback 使用了 Element 类型而不是 Component，这样的写法可以使开发者更加方便的注入想要的 props Using elements instead of components means we don't have to provide a passProps-style API so you can get the props you need to your elements. 使用 Element 类型代表着 router 不再需要提供类似于`passProps`的 API，开发者可以直接将 props 注入到组件中。 另外，原文中提到在 v5 版本下想要自定义 props，要么使用`passProps`，要么使用`render props`或者`hoc`。唉 又一个时代的陨落 😌 // 简单写法 <Route path=":id" component={Profile} /> // v6写法 <Route path=":id" element={<Profile />} /> // 需要传入props时 <Route path=":id" render={() => (<Profile animate={true} />)} /> // v5.1写法 <Route path=":id" children={<Profile animate={true} />} /> // v6写法 <Route path=":id" element={<Profile animate={true} />} /> Another important reason for using the element prop in v6 is that is reserved for nesting routes. emmm...恢复 v3 版本的嵌套路由写法，👴🏻 青回 const App = () => { return ( <BrowserRouter> <Routes> <Route path="/" element={<Home />} /> <Route path="users" element={<Users />}> <Route path="me" element={<OwnUserProfile />} /> <Route path=":id" element={<UserProfile />} /> </Route> </Routes> </BrowserRouter> ); }; const User = () => { return ( <div> <nav> {/\* v6中会自动跳转到/users/me，这个点需要注意 \*/} <Link to="me">My Profile</Link> </nav> </div> ); }; 在子路由中直接使用 Link 跳转将默认带上父路由前缀，这个改动要十分注意，算是第三个比较大的破坏性变更 的注意事项 React Router v6 uses a simplified path format. in v6 supports only 2 kinds of placeholders: dynamic :id-style params and \_ wildcards. 在 v6 版本中 path 的有效写法： /groups /groups/admin /users/:id /users/:id/messages /files/\* /files/:id/\* 不再支持以下正则写法： /users/:id? /tweets/:id(\\d+) /files/\*/cat.jpg /files-\* One other thing to notice is that all path matching in v6 ignores the trailing slash on the URL. v6 版本中会忽略掉 path 的尾部斜杠，意味着和两种写法将被认为是同一个路由 的注意事项 In v5, a value that does not begin with / was ambiguous; it depends on what the current URL is. 上文有提到这个变更，这里刚好可以深入描述下， 对于 v5 版本的的解释，原文举例如下： 当前 url 为`/users`时，会被解析为`<a href="/me" />` 当前 url 为`/users/`时，会被解析为`<a href="/users/me" />` router 团队在 v6 中消除了这种歧义模糊的写法，在`<Route path="users">`下，无论当前 url 是否拥有后斜杠，`<Link to="me">`将始终被渲染为`<a href="/users/me" />`。 同时 Link 支持`..`这样的 leading segment 来跳转其他相对路由 // users路由 const User = () => { return ( <div> {/\* 跳转当前路由 /users \*/} <Link to=".">Users</Link> {/\* 跳转路由 /users/:id \*/} <Link to={user.id}>{user.name}</Link> </div> ); }; // users/:id 路由下 const UserPrfile = () => { return ( <div> {/\* 返回父级路由 /users \*/} <Link to="..">Return Users</Link> {/\* 跳转路由 /users/mj \*/} <Link to="../mj">MJ</Link> </div> ); }; 使用 useRoutes 代替 react-router-config 包 All of the functionality from v5's react-router-config package has moved into core in v6. 通常在项目中并不会通过 JSX 的方式来声明 Route，大多数会沉淀成一个 config 文件并通过一个 map 方法自动渲染。 而上述场景最常使用的包就是`react-router-config`（很早之前 umi 中就是利用这个包解析 config 中的路由配置） 但是在 v6 版本中默认集成了这种 API，真是喜大普奔的一件事。 const App = () => { const elements = useRoutes(\[ { path: '/', element: <Home /> }, { path: 'dashboard', element: <Dashboard /> }, { path: 'invoices', element: <Invoices />, // 通过children属性实现嵌套路由 children: \[ { path: ':id', element: <Invoice /> }, { path: 'sent', element: <SentInvoices /> }, \], }, // 通过\*通配符实现404页面 { path: '\*', element: <NotFound /> }, \]); }; 使用 useNavigate 代替 useHistory React Router v6 introduces a new navigation API that is synonymous with and provides better compatibility with suspense-enabled apps. 这是第三个破坏性变更：废弃`useHistory`，由`useNavigate`代替 v6 版本写法 const App = () => { const navigate = useNavigate(); const handleClick = () => { navigate('/home'); }; return ( <div> <button onClick={handleClick}>返回首页</button> </div> ); }; 主要写法变更： `history.push("/")` => `navigate("/")` `history.replace("/")` => `navigate("/",{ replace: true })` `history.goBack()` => `navigate(-1)` `history.goForward()` => `navigate(1)` `history.go(2)` => `navigate(2)` 另外，navigate 还支持与 Link 相同的相对路径写法 Link 不再支持 component 属性 First of all, a should pretty much always render an . If yours does not, there's a good chance your app has some serious accessibility and usability problems, and that's no good. 其实就是希望标签更加语义化 NavLink 的 exact 属性替换为 end 基本没用过，没什么可说的 // v5 <NavLink exact> // v6 <NavLink end> NavLink 删除 activeClassName 属性和 activeStyle 属性 在 v6 版本中，NavLink 的 style 和 className 支持函数写法，所以移除了这两个属性 <NavLink style={({isActive}) => ({color:"#1890FF"})} className={({isActive}) => "active"}> StaticRouter 方法被移动到react-router-dom/server包中 // v5 import { StaticRouter } from 'react-router-dom'; // v6 import { StaticRouter } from 'react-router-dom/server'; 使用 useMatch 替换 useRouteMatch 新的 useMatch 方法同样使用了「智能匹配算法」，同时 API 也更改为 v6 中的几个命名 感兴趣可以直接查看[API 文档](https://github.com/remix-run/react-router/blob/main/docs/api.md#usematch) 最后总结 最后按惯例总结下全文的核心内容： 升级的前提： router 团队建议先升级到 5.1 版本并使用新的 API 来渐进式升级到 v6 不再兼容 react v16.8 以下版本 主要改动内容： 废弃 `Switch` 组件，由 `Routes` 代替（使用了智能匹配路径算法） 废弃 `Redirect` 组件，由 `Navigate` 代替 废弃 `useHistory` 方法，由 `useNavigate` 代替 `Route` 组件移除原有 `component` 及 `render` 属性，统一通过 `element` 属性传递：`<Route element={<Home />}>` `Route` 组件支持嵌套写法（v3 版本用法回归） `Route` 组件的 `path` 规则变更，不再支持类正则写法 消除了 `v5` 版本中带后斜杠的路径时，`Link` 组件的跳转模糊的问题 `Link` 组件支持自动携带当前父路由，以及相对路径写法`../home` 新增 `useRoutes` 方法，代替之前的`react-router-config`写法，同样支持嵌套 其他一些 API 名称变更 **最后祝各位老板身体健康！升官发财！！！**

---

*Originally published on [zksyncV2](https://paragraph.com/@zksyncv2/react-router-v6)*
