likes
comments
collection
share

React Router V5升级V6 简单指南把我们项目的 React Router 从 V5 升级到 V6 版本。我

作者站长头像
站长
· 阅读数 59

前言

最近都在做项目重构的事情,借着这次机会统一技术栈,准备把我们项目的 React Router 从 V5 升级到 V6 版本。

React Router V5升级V6 简单指南把我们项目的 React Router 从 V5 升级到 V6 版本。我

React Router: reactrouter.com/en/main/upg…

React Router 版本 6 引入了几个强大的新功能,并改进了与最新版本的 React 的兼容性。它还引入了版本 5 中的一些重大更改,但不得不说 某些 API 的变化还是很大的,想要升级必然需要投入点人力对 API 完成替换,好在替换的东西不难只是类别多,整理清楚知道要替换什么就好。

本文就以我这次升级为例,简单整理一下我都更改了哪些 API。

准备

  1. 首先我们使用的 React 版本需要高于 16.8,因为 React Router v6 大量使用了 React Hooks。

  2. 然后你使用的 React router 版本最好高于 5.1,这样有利于我们做一些过渡性的工作,如果你还是 V4,那么你可能需要先升级到 V5 验证你项目的可行性。

  3. 要从 V4/5 升级到 5.1,通常需要完成

    3.1 使用 <Route children> 代替 <Route render><Route component> props

    3.2 使用 Hooks API 来访问路由器状态,如 useLocation 和 useParam

    3.3 不要再使用 withRouter 来生成路由组件,而是在组件中使用 hooks 方法来获取路由信息

1. 将 Switch 组件升级为 Routers

Router v6 引入了 Routes 组件,不再有 Switch 了。Routes 组件将根据最佳匹配选择路线,而不是按顺序遍历,所以我们不用在费心去调整 Route 的顺序或者指定精确匹配了。

同时,如果你存在嵌套路由,想要在子组件中获取路由信息,你需要使用match.urlmatch.path。此外,如果你还有更深的嵌套路由,你需要在子组件的路由中继续写入 Route,层级较深的时候则变得不那么直观了。

如果你已经升级到 v5.1,并完成了 Switch 替换,那么这一步就简单了,如果没有,就一起来替换吧。

如下方替换前和替换后的代码所示,对比起来确实简洁明了多了,主要的变化如下

  1. 组件被替换为组件,同时嵌套的也可以被去掉了,改用包裹两个 Route 就行。
  2. 路由匹配规则 path 的编写,/page2 -> page2/* ;`${match.path}/:id` -> :id ,<Route exact>被取消了,相反,具有后代路由(在其他组件中定义)的路由在其路径 * 中使用尾随来表示它们深度匹配。
  3. children/component -> element, 现在需要通过 element 来指示路由走向的组件
  4. 在 V6 中,被 Routes 包裹的 Route 不用再显示或隐式传入 router props,框架会自动完成
  5. 当然这些是最基础的更改,如果你还使用了类似 render 传参,你可以继续往下看

替换前:

// This is a React Router v5 app
import {
  BrowserRouter,
  Switch,
  Route,
  Link,
  useRouteMatch,
} from "react-router-dom";

const App = () => {
  return (
    <BrowserRouter>
      <Switch>
        <Route exact path="/">
          <Page1 />
        </Route>
        <Route path="/page2">
          <Page2 />
        </Route>
      </Switch>
    </BrowserRouter>
  );
}

const Page2 = () => {
  let match = useRouteMatch();

  return (
    <div>
      <nav>
        <Link to={`${match.url}/photo`}>My Photo Info</Link>
      </nav>
      <Switch>
        <Route path={`${match.path}/nameEdit`}>
          <NameEdit />
        </Route>
        <Route path={`${match.path}/:id`}>
          <UserInfo />
        </Route>
      </Switch>
    </div>
  );
}

替换后:

// This is a React Router v6 app
import {
  BrowserRouter,
  Routes,
  Route,
  Link,
} from "react-router-dom";

const App = () => {
  return (
    <BrowserRouter>
      <Routes>
        <Route path="/" element={<Page1 />} />
        <Route path="page2/*" element={<Page2 />} />
      </Routes>
    </BrowserRouter>
  );
}

const Users = () => {
  return (
    <div>
      <nav>
        <Link to="photo">My Photo Info</Link>
      </nav>

      <Routes>
        <Route path=":id" element={<UserInfo />} />
        <Route path="nameEdit" element={<NameEdit />} />
      </Routes>
    </div>
  );
}

2. 停用 Route 组件中的 render

在 v6 以前,我们想要往子组件中传递路由信息或者传递自定义的参数,我们通常需要借助 render,现在不用了,routerV6 会自动传入路由的 props,我们只需要自己处理自定义参数就好了

// V5
<Route
  key={info.id}
  path={`/info/${info.id}`}
  render={(props) => {
    return <InfoPage {...props} attrs={info} />;
  }}
/>
<Route
  key="detail"
  path="/user/detail"
  render={(props) => {
    return <Detail {...props} />;
  }}
/>
// V6
<Route key={info.id} path={`/info/${e.id}`} element={<InfoPage attrs={e} />} />
<Route key="detail" path="/user/detail" element={<Detail />} />

3. 停止使用 withRouter

在 V6 中,我们需要去除通过 WithRouer 来创建路由组件的方法,转而在 Routes 包裹的组件中使用 Hooks 直接获取就可以了,是不是很方便。

做完了第 2 点,这一步也就容易理解了。

// 现在我们编写InfoPage组件不必再使用withRouter包裹高阶组件
// 直接使用hooks获取信息即可
function InfoPage({ attrs }) {
  let params = useParams();
  let location = useLocation();
}

4. 相关的变化

Router V5/总是很有歧义,取决于当前的 URL 是什么。例如:

当前 URL 为 /info,则 则指向 '/me'

当前 URL 为 /info/,则 则指向 '/info/me'

结合嵌套较深的路由,则这种相对路由的用法就有点难以明确当前的路由到底指向什么,所以在 V5 中我们通常都会使用 match.urlroot URL 构建路由

在 V6 中,router 统一明确了这个问题,在 Route 包裹下的 <Link to="me"> 将总是指向/info/me,这这个情况下,如果我们想使用父路由,使用 ..指示即可。

例如:

// 当前路由 /app/info
// a trailing slash)
<Link to="detail">               => <a href="/app/info/detail">
<Link to="../detail">            => <a href="/app/detail">
<Link to="../../detail">         => <a href="/detail">
<Link to="../../../detail">      => <a href="/detail">

Link 还有第二个变化,关于 to\pathname\state, 其实也就是去掉了 pathname,直接写在 to 中,state 现在是一级 props 来传递

// V5
<Link to={{ pathname: "/info", state: state }} />
// V6
<Link to="/info" state={state} />

5. 使用 useNavigate 替换 useHistory

变化很简单,但是可能涉及到的地方比较多,替换的时候要小心一些。

// V5 
import { useHistory } from "react-router-dom";
const App = () => {
  const history = useHistory();
  return (
    <div>
      <button onClick={() => { history.push("/info") }}>go Info</button>
    </div>
  );
}

// V6
import { useNavigate } from "react-router-dom";
const App = () => {
  const navigate  = useNavigate();
  return (
    <div>
      <button onClick={() => { navigate("/info") }}>go Info</button>
    </div>
  );
}

更多的:

你想使用 replace: navigate(to, { replace: true })

你想传递 state:navigate(to, { state })

当然,除了编程式导航,也可以使用声明式导航:

import { Navigate } from "react-router-dom";

function App() {
  return <Navigate to="/info" replace state={state} />;
}

相关 API 变更,使用 navigate 加数字直接跳转即可:

// V5 
const { go, goBack, goForward } = useHistory();
// V6
const navigate = useNavigate();
navigate(-1)
navigate(1)
navigate('/url')

结尾

至此,我完成了这些更改,我的项目已经顺利的跑起来了,其他大部分的更改看似是破坏性的,其实变更并不复杂,概念并没有变化。我认为,需要着重理解的就是我们在写 Route 的时候 关于 path的编写逻辑需要确定一下。

转载自:https://juejin.cn/post/7426696679245905974
评论
请登录