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。
准备
-
首先我们使用的 React 版本需要高于 16.8,因为 React Router v6 大量使用了 React Hooks。
-
然后你使用的 React router 版本最好高于 5.1,这样有利于我们做一些过渡性的工作,如果你还是 V4,那么你可能需要先升级到 V5 验证你项目的可行性。
-
要从 V4/5 升级到 5.1,通常需要完成
3.1 使用
<Route children>
代替<Route render>
或<Route component>
props3.2 使用 Hooks API 来访问路由器状态,如 useLocation 和 useParam
3.3 不要再使用 withRouter 来生成路由组件,而是在组件中使用 hooks 方法来获取路由信息
1. 将 Switch 组件升级为 Routers
Router v6 引入了 Routes
组件,不再有 Switch 了。Routes 组件将根据最佳匹配选择路线,而不是按顺序遍历,所以我们不用在费心去调整 Route 的顺序或者指定精确匹配了。
同时,如果你存在嵌套路由,想要在子组件中获取路由信息,你需要使用match.url
和 match.path
。此外,如果你还有更深的嵌套路由,你需要在子组件的路由中继续写入 Route,层级较深的时候则变得不那么直观了。
如果你已经升级到 v5.1,并完成了 Switch 替换,那么这一步就简单了,如果没有,就一起来替换吧。
如下方替换前和替换后的代码所示,对比起来确实简洁明了多了,主要的变化如下
- 组件被替换为组件,同时嵌套的也可以被去掉了,改用包裹两个 Route 就行。
- 路由匹配规则 path 的编写,
/page2 -> page2/* ;`${match.path}/:id` -> :id ,<Route exact>被取消了,相反,具有后代路由(在其他组件中定义)的路由在其路径 * 中使用尾随来表示它们深度匹配。
children/component -> element
, 现在需要通过 element 来指示路由走向的组件- 在 V6 中,被 Routes 包裹的 Route 不用再显示或隐式传入 router props,框架会自动完成
- 当然这些是最基础的更改,如果你还使用了类似 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.url
从 root 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