react-router-dom5如何升级到6
作者:田本初
前言
升级前版本为5.1.2
下载与运行
下载
npm install react-router-dom@6
运行
运行发现报错:
将node_modules删除,重新执行npm i即可
运行发现如下报错:
这是因为之前有引用react-router-dom.min,v6中取消了该文件,所以未找到文件导致报错。
将引用变为react-router-dom即可解决。
再次运行发现如下报错:
这是因为react-router-dom6取消了withRouter,如果想要获取路由地址,需要使用useLocation
变为
再次运行发现如下报错:
按照官网进行如下修改:
Switch 被替换为 Routes
v5
时代的第一个被替代的是Switch组件
。
该Switch
组件用于包装我们的路由,它确保每次只加载一个匹配的路由。
但这在 v6 中不再存在。我们使用Routes
组件来替换Switch
。
请注意,我们仍然需要导入BrowserRouter
包装我们的应用程序,就像在 v5 中所做的那样。
在 v5 中,我们是这样做:
import { BrowserRouter, Switch } from "react-router-dom"; function App() { return ( <BrowserRouter> <div className="App"> <Switch> {/* 路由Route在此定义 */} </Switch> </div> </BrowserRouter> ); } export default App
但在 v6 中,我们将这样做
import { BrowserRouter, Routes } from "react-router-dom"; function App() { return ( <BrowserRouter> <div className="App"> <Routes> {/* Switch 会被改成 Routes */} {/* 路由Route在此定义 */} </Routes> </div> </BrowserRouter> ); } export default App
注意:v5的Switch中可以包含非Route元素,但Routes中只能存在Route,否则会报错。
Route组件使用更新
尽管该Route组件在 v6
中仍然保留一个位置,但我们定义它的使用方式与我们在 v5
中的方式不同。
我们将不再以 v5
中的任何方式放置我们想要渲染的组件,而是统一将其作为element
prop的值传递。
没有exact配置
在 v5
中,不添加exact
作为Route组件
的props
的话,如果 URL 以 path 关键字开头,则路径将匹配,因为匹配过程是从上到下的顺序。
但在 v6 中,我们将不再需要该exact
配置,因为路径模式匹配算法已更改,并且现在更加增强。
在 v5 中,我们这样做了:
<Switch> {/* 三种Route组件使用定义 */} <Route path="/signup" component={Product} /> {/* 或 */} {/* 这个方法允许我们将 props 传递给渲染的组件 */} <Route path="/games"> <Product id={2} /> </Route> {/* 或是通过render函数 */} <Route path="/games" render={(props) => <Product {...props} />} /> </Switch>
在 v6 中,
<Routes> {" "} <Route path="/games" element={<Product />} /> {/* 带有props的渲染组件 */} <Route path="/movies" element={<Product id={200} category="shirt" />} /> </Routes>
Links 和 NavLinks
Link
和NavLink
组件仍然可以运行在V6。Link
组件使用与在 v5 的时候保持一样,但使用NavLink
组件时,删除了activeClassName
和activeStyle
prop。在 v5 中,activeClassName
prop 用于在链接激活后自动将一些 CSS
类应用于链接,同时activeStyle
允许我们在链接激活时向链接添加内部样式。
但是在 v6 中,现在可以使用一个函数来获取有关链接活动状态的信息。该函数的参数是一个具有属性的对象isActive
。此属性在链接处于活动状态时为真,在非活动时为假。isActive
的值允许我们使用条件表达式来指示活动样式或类名。
在 v5 中,
import {NavLink} from “react-router-dom” {/* … */} <NavLink to="/product" style={{ color: "#689" }} activeStyle={{ color: "#3072c9" }} className="nav_link" activeClassName="active" > Products </NavLink>
但在 v6 中,我们将这样做:
<NavLink to="/product" style={({ isActive }) => ({ color: isActive ? "#3072c9" : "#689" })} className={({ isActive }) => `link${isActive ? " active" : ""}`} > Product </NavLink>
Navigate替代Redirect
在 v5 中,使用该Redirect
组件将一个页面带到另一个页面,但它不再从 v6
中的 react-router-dom
导出。它已被Navigate
组件替换。
在 v5 中,
<Route path="/faq"> <Redirect to="/about" /> </Route> <Route path="/about" component={About} />
但在 v6 中,
<Route path="/games" element={<Navigate to="/about" />} />; <Route path="/games" element={<About />} />;
需要注意的是,如果只是按照Navigate上面代码片段中的方式添加组件,它只会将导航到该路径的导航推送到导航堆栈中,但是如果我们打算用新页面替换当前页面,我们将 replace 属性添加到Navigate组件中,如下所示:
<Route path="/games" element={<Navigate replace to="/about" />} />;
嵌套路由
顾名思义,嵌套路由是放置在另一个路由中的路由。它们用于在子组件中呈现更具体的信息。在 v6 中,将嵌套路由放置为父路由的子路由。然后引入Outlet
组件,它是从渲染组件中的 react-router-dom
导出的,用于指定希望嵌套信息显示在哪里。Outlet
组件不是必需的,但它使代码更清晰。
在 v5 中,
import { useRouteMatch } from "react-router-dom"; function App() { return ( <BrowserRouter> <Switch> <Route exact path="/about" component={About} /> <Route path="/product" component={Product} /> </Switch> </BrowserRouter> ); } function Product() { let match = useRouteMatch(); return ( <div> <Switch> {/* match.path 返回父路由中指定的路径。在这种情况下,它是“/product" */} <Route path={`${match.path}`}> <AllProducts /> </Route> {/* 匹配 /product/:id */} <Route path={`${match.path}/:id`}> <ProductDetail /> </Route> </Switch> </div> ); }
在 v6 中,
import { Outlet } from "react-router-dom"; function App() { return ( <Routes> <Route path="/about" element={<About />} /> <Route path="/product" element={<Product />}> {/* 这里嵌套路由的路径是相对于父路由的路径的。 */} {/* 这里变成 "/product/" */} <Route path="/" element={<AllProducts />} /> {/* 这里变成 "/product/:id" */} <Route path="/:id" element={<ProductDetail />} /> </Route> </Routes> ); } function Product() { return ( <Container> <> <div>Product</div> {/* 父组件的其他内容 */} </> {/* 这是嵌套信息开始的地方 */} <Outlet /> </Container> ); }
useNavigate代替useHistory
当用户因路径上发生的事件(例如单击按钮、API 请求完成等)而被重定向时,就会发生程序化导航。在 v5 中,可以使用useHistory
钩子来执行以下操作:
import { useHistory } from "react-router-dom"; function Product() { const history = useHistory(); const handleClick = () => { //这会将新路线推送到导航堆栈的顶部 history.push("/new-route"); history.push({pathname:"/new-route",state:{p1:xxx}); // 传参 //这会将当前路线替换为导航堆栈中的新路由 history.replace("/new-route"); }; return ( <div> <button>点击我重定向到新路由</button> </div> ); }
但是在 v6 中,useHistory
hook 被替换为useNavigate
hook,并且以不同的方式使用它。
import { useNavigate } from "react-router-dom"; function Product() { const navigate = useNavigate(); const handleClick = () => { //这会将新路线推送到导航堆栈的顶部 navigate("/new-route"); navigate("/new-route",{state:{p1:xxx}}); // 传参 //这会将当前路线替换为导航堆栈中的新路由 navigate("/new-route", { replace: true }); }; return ( <div> <button>点击我重定向到新路由</button> </div> ); }
一件很酷的事情是以在导航堆栈上任意前进和后退。通过使用正数作为上述参数navigate()
,路由会向前移动该步数。负数向后做同样的事情
// Goes forward navigate(1) // Goes forward twice navigate(2) // Goes backward navigate(-1) // Goes backward three times navigate(-3)
删除Prompt组件
Prompt如果有未保存的更改,v5 中的组件可防止意外离开页面。但是 react-router
团队并没有将它包含在 v6
中,也没有替代方案。因此,如果你需要该功能,你可以手动实现它或返回到 v5。
除了不包括Prompt
在当前版本(v6)中,useBlocker
和usePrompt
都不起作用。react-router
团队虽然在官方文档中表示,他们目前正在努力将其添加回 v6
,但不是针对 6.x 的第一个稳定版本。
注意
react-router-dom5的Switch可以嵌套Redirect
但react-router-dom6的Routes中只可以存在Route和Route.Fragment
v5的Router写法,需要利用history
import { createBrowserHistory } from "history" const customHistory = createBrowserHistory() const Root = () => { return ( <Router history={customHistory}> ...... </Router> ) }
v6直接更改路由引入方式即可
import { BrowserRouter } from "react-router-dom" const Root = () => { return ( <BrowserRouter> ...... </BrowserRouter> ) }
总结
以上为个人经验,希望能给大家一个参考,也希望大家多多支持脚本之家。