从 React Router v5 过渡到 v6
React-router 是 react js 中路由的标准库。它允许 React 应用程序的用户在应用程序的不同部分(组件)之间移动。
react-router 团队 宣布将 在 2021 年底发布react-router 版本 6 (v6) 的稳定版本,但由于一些重大的 API 更改,从 react-router 版本 5 (v5) 切换到 v6 可能会很困难. 在本文中,我们将介绍 v6 中的新功能以及如何将现有的 React 项目从 v5 升级到 v6。
要在我们的应用程序中升级 react-router 包的版本,我们导航到项目文件夹并运行
npm install react-router-dom@[VERSION_NUMBER]
替换VERSION_NUMBER为我们要安装的版本,或者如果我们想要最新版本,则替换为“ latest ”,如下所示:
npm install react-router-dom@6
或者
npm install react-router-dom@latest
请注意,我们必须连接到互联网才能完成安装,否则安装将失败。另外,请确保项目中的 react 版本是 v16.8 或更高版本,因为 react-router v6 严重依赖于 react v16.8 最初支持的钩子
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
Route组件使用更新
尽管该Route组件在 v6 中仍然保留一个位置,但我们定义它的使用方式与我们在 v5 中的方式不同。我们将不再以 v5 中的任何方式放置我们想要渲染的组件,而是统一将其作为elementprop的值传递。
没有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 中,activeClassNameprop 用于在链接激活后自动将一些 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={} />;
嵌套路由
顾名思义,嵌套路由是放置在另一个路由中的路由。它们用于在子组件中呈现更具体的信息。在 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>
);
}
程序化导航
当用户因路径上发生的事件(例如单击按钮、API 请求完成等)而被重定向时,就会发生程序化导航。在 v5 中,我们可以使用useHistory钩子来执行以下操作:
import { useHistory } from "react-router-dom";
function Product() {
const history = useHistory();
const handleClick = () => {
//这会将新路线推送到导航堆栈的顶部
history.push("/new-route");
//这会将当前路线替换为导航堆栈中的新路由
history.replace("/new-route");
};
return (
<div>
<button>点击我重定向到新路由</button>
</div>
);
}
但是在 v6 中,useHistoryhook 被替换为useNavigatehook,并且我们以不同的方式使用它。
import { useNavigate } from "react-router-dom";
function Product() {
const navigate = useNavigate();
const handleClick = () => {
//这会将新路线推送到导航堆栈的顶部
navigate("/new-route");
//这会将当前路线替换为导航堆栈中的新路由
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 的第一个稳定版本。
概括
让我们强调一下我们所经历的变化。
Switch 组件替换为 Routes 组件。
如何放置 Route 的渲染组件的更改。
路由中没有exact。
activeClassName和activeStyle不存在于NavLink组件了.
我们可以通过函数回调访问 NavLink 组件的 isActive 状态。
Redirect组件已替换为Navigate组件。
实现嵌套路由的一种更时尚的方式。
没有Prompt组件
总之,如果你还没有准备好从 v5 或任何其他版本切换到 v6,你可以继续使用它安装以前的版本。
npm install react-router-dom@[VERSION_NUMBER]
但是,你将错过 v6 附带的一些好东西,包括但不限于:
增强的路径模式匹配算法。
根据Bundlephobia,体积大小减少了 60%
我相信我们能够成功地切换到 react-router v6 并停止使用 Switch 组件(双关语非常有意)
作者:xiaobin1213
欢迎关注微信公众号 :前端晚间课
更多文章,收录于小程序-互联网小兵