官网:React Router
在进行项目时,遇到了React Router升级导致的问题,故特地找来官方API研究,但官方文档还未翻译,借助对V5的理解与翻译软件的帮助对官网的《Upgrading from v5》进行翻译记录。这一篇主要是记录React Router 版本变化,但由于个人开发经验所限,在记录的过程中省去了不熟悉的内容,详细的还是得参考官网。由于基本是把官网粗略的“翻译”了一下,故篇幅较多,而且问题不少,望指出我的不足之处。其中也有还没有实践过的内容。
总的来说,我能感知和理解的v6具体变化具体是以下四个:
替换为
的改变
的改变
替换为
V6大量使用了React Hooks,所以需要使用 React 16.8 或更高版本。
可以平滑的过渡到V6
中的
如果想重定向,将
移动到
prop 中。
// 更改前
<Switch>
<Redirect from="about" to="about-us" />
</Switch>
// 更改后
<Switch>
<Route path="about" render={() => <Redirect to="about-us" />} />
</Switch>
不在
内的
元素得到保留。他们将在V6中成为元素。
略。
先安装v6版:
$ npm install react-router-dom
升级到
v6 引入了一个类似于Switch
但更强大的组件Routes
。
要使用 v6,需要将所有
元素转换为
。如果已经升级到 v5.1,那么已经完成了一半
在v5中,必须非常明确地说明希望如何嵌套路由和链接(nest routes and links)。在这两种情况下,如果想嵌套路由和链接(nested routes and links),则必须从父路由(parent route’s)的match.url
和match.path
属性构建
和 。
此外,如果要嵌套路由(nest routes),则必须将它们放在子路由的组件中。
V5与V6对比:
在此示例中,有关 v6 需要注意的一些重要事项:
和
是相对的。这意味着它们会自动在父路由的路径和URL上构建,因此不必手动插值或match.url
或match.path
。
消失了。相反,具有后代路由(在其他组件中定义)的路由在其路径中使用一个尾随*
符号来指示它们精确匹配
。
。注意: v5 应用中的所有
内容在 v6中都已更改为
。
由此我们可以引出
的优点。
的优点在 v6 中使用element
prop 的另一个重要原因是该
是为嵌套路由保留的。将上一示例中的代码更进一步,我们可以将所有
元素提升到单个路由配置中:
更新对比:
// 此处没有变化
<Route path=":userId" component={Profile} />
// v4和v5
<Route
path=":userId"
render={routeProps => (
<Profile routeProps={routeProps} animate={true} />
)}
/>
// v6为:
<Route path=":userId" element={<Profile animate={true} />} />
<Route
path=":userId"
children={({ match }) => (
match ? (
<Profile match={match} animate={true} />
) : (
<NotFound />
)
)}
/>
// v6为:
function Profile({ animate }) {
let params = useParams();
let location = useLocation();
}
// v4和v5
function DeepComponent(routeStuff) {
// got routeStuff, phew!
}
// v6写法:
function DeepComponent() {
// oh right, same as anywhere else
let navigate = useNavigate();
}
export default withRouter(DeepComponent);
在 v6 中使用element
prop 的另一个重要原因是是为嵌套路由保留的。这是人们最喜欢的 v3 和@reach/router
功能之一。将上一示例中的代码更进一步,我们可以将所有
元素提升到单个路由配置中:
import {BrowserRouter,Routes,Route,Link,Outlet} from "react-router-dom";
function 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>
);
}
function Users() {
return (
<div>
<nav>
<Link to="me">My Profile</Link>
</nav>
<Outlet />
</div>
);
}
此步骤是可选的。
请注意
元素如何自然地嵌套在
元素中。嵌套路由通过添加到父路由的路径来构建其路径。我们这次不需要在上尾随*
,因为当routes
在一个位置定义时,路由器能够看到所有嵌套的路由。``
只有当该路线的后代树中有另一个
位置时,您才需要尾随*
。在这种情况下,后代
将在路径名中剩余的部分上进行匹配(有关这在实践中的外观,请参阅前面的示例)。
使用嵌套配置时,children
路由应呈现
以便呈现其子路由。这使得使用嵌套 UI 呈现布局变得容易。
模式说明v6 使用简化的路径格式。
在 v6 中,仅支持 2 种占位符:
*
通配符。*
通配符只能在路径的末尾使用,不能在中间使用。以下所有内容都是 v6 中的有效路由路径:
/groups
/groups/admin
/users/:id
/users/:id/messages
/files/*
/files/:id/*
以下正则表达式样式的路由路径在 v6 中无效:
/users/:id?
/tweets/:id(\d+)
/files/*/cat.jpg
/files-*
**注意:**v6 中的所有路径匹配都会忽略 URL 上的尾随斜杠。实际上,
已被删除,在 v6 中没有效果。这并不意味着如果需要,不能使用尾随斜杠。应用可以决定是否使用尾部斜杠,只是不能在
和
处呈现两个不同的 UI客户端。您仍然可以在这些URL上呈现两个不同的UI(但不建议这样做),但 必须 在服务器端执行此操作。
模式说明在 v5 中,不以\
开头的值是模棱两可的;这取决于当前 URL 是什么。
例如,如果当前 URL 为/users
,则 v5 的将render成
。但是,如果当前 URL 具有尾部斜杠如
/users/
,则相同的将render为
。这使得很难预测链接的行为方式,因此在 v5 中,建议从根(root) URL(使用
match.url
)构建链接,而不要使用相对值。
V6修复了这种歧义。在 v6 中,无论当前 URL 如何,将始终render相同的
。
例如,在
内呈现的将始终render一个指向
的链接,而不管当前 URL 是否具有尾随斜杠。
当想将"向上"链接回父路由时,请在值中使用
..
前导段,类似于在操作。
function App() {
return (
<Routes>
<Route path="users" element={<Users />}>
<Route path=":id" element={<UserProfile />} />
</Route>
</Routes>
);
}
function Users() {
return (
<div>
<h2>
{/* This links to /users - the current route */}
<Link to=".">Users</Link>
</h2>
<ul>
{users.map(user => (
<li>
{/* This links to /users/:id - the child route */}
<Link to={user.id}>{user.name}</Link>
</li>
))}
</ul>
</div>
);
}
function UserProfile() {
return (
<div>
<h2>
{/* This links to /users - the parent route */}
<Link to="..">All Users</Link>
</h2>
<h2>
{/* This links to /users/:id - the current route */}
<Link to=".">User Profile</Link>
</h2>
<h2>
{/* This links to /users/mj - a "sibling" route */}
<Link to="../mj">MJ</Link>
</h2>
</div>
);
}
将当前 URL 视为文件系统上的目录路径,就像
cd
命令行实用程序一样。
// If your routes look like this
<Route path="app">
<Route path="dashboard">
<Route path="stats" />
</Route>
</Route>
// and the current URL is /app/dashboard (with or without
// a trailing slash)
<Link to="stats"> => <a href="/app/dashboard/stats">
<Link to="../stats"> => <a href="/app/stats">
<Link to="../../stats"> => <a href="/stats">
<Link to="../../../stats"> => <a href="/stats">
// On the command line, if the current directory is /app/dashboard
cd stats # pwd is /app/dashboard/stats
cd ../stats # pwd is /app/stats
cd ../../stats # pwd is /stats
cd ../../../stats # pwd is /stats
useRoutes
替代react-router-config
v5的react-router-config
包中的所有功能都已迁移到 v6 的核心中。如果你喜欢/需要将路由定义为 JavaScript 对象,而不是使用 React 元素,你一定会喜欢这个。
function App() {
let element = useRoutes([
// These are the same as the props you provide to
{ path: "/", element: <Home /> },
{ path: "dashboard", element: <Dashboard /> },
{
path: "invoices",
element: <Invoices />,
// Nested routes use a children property, which is also
// the same as
children: [
{ path: ":id", element: <Invoice /> },
{ path: "sent", element: <SentInvoices /> }
]
},
// Not found routes work as you'd expect
{ path: "*", element: <NotFound /> }
]);
// The returned element will render the entire element
// hierarchy with all the appropriate context it needs
return element;
}
替代
v5版本写法:
import { useHistory } from "react-router-dom";
function App() {
let history = useHistory();
function handleClick() {
history.push("/home");
}
return (
<div>
<button onClick={handleClick}>go home</button>
</div>
);
}
在V6版本中,使用navigate
API。大多数情况下,这意味着更改useHistory
为useNavigate
并且更改history.push
或history.replace
。
v6版本写法:
import { useNavigate } from "react-router-dom";
function App() {
let navigate = useNavigate();
function handleClick() {
navigate("/home");
}
return (
<div>
<button onClick={handleClick}>go home</button>
</div>
);
}
navigate(to, { replace: true })
。navigate(to, { state })
。你可以把第一个参数
想象成,其他的参数是
replace
和state
。
如果更喜欢使用声明性 API(declarative API)进行导航(navigation)(v5的Redirect
组件),v6 提供一个
组件。像这样使用:
import { Navigate } from "react-router-dom";
function App() {
return <Navigate to="/home" replace state={state} />;
}
注意(差异):
默认使用replace
逻辑,可通过push
prop 进行更改。
默认使用push
逻辑,可通过replace
prop 进行更改。// 过去写法:
<Redirect to="about" />
<Redirect to="home" push />
// v6写法:
<Navigate to="about" replace />
<Navigate to="home" />
简单来说,v5中从useHistory
中使用go、goBack、goForward实现来回、指定跳转等,升级为只需要使用navigate
。
代码对比如下:
例如,下面是一些使用 v5 的useHistory
钩子的代码:
import { useHistory } from "react-router-dom";
function App() {
const { go, goBack, goForward } = useHistory();
return (
<>
<button onClick={() => go(-2)}>
Go 2 pages back
</button>
<button onClick={goBack}>Go back</button>
<button onClick={goForward}>Go forward</button>
<button onClick={() => go(2)}>
Go 2 pages forward
</button>
</>
);
}
V6写法:
import { useNavigate } from "react-router-dom";
function App() {
const navigate = useNavigate();
return (
<>
<button onClick={() => navigate(-2)}>
Go 2 pages back
</button>
<button onClick={() => navigate(-1)}>Go back</button>
<button onClick={() => navigate(1)}>
Go forward
</button>
<button onClick={() => navigate(2)}>
Go 2 pages forward
</button>
</>
);
}
注意: 出于安全性考虑,不再支持将 v5 中的
元素作为路由配置的一部分(包含在
中)。如果需要立即重定向(redirect),可以
a方案:在服务器上执行此操作(可能是最佳解决方案)
b方案:在路由组件中render
元素。但是,要认识到导航(navigation)将发生在useEffect
中。
略
为
这是对 prop 的简单重命名,以便更好地与 React 生态系统中其他库的常见做法保持一致。
activeClassName
和activeStyle
这两个props变化:
可以通过一个函数,根据组件的活动状态自定义内联样式(style
)或类字符串(className
)。
<NavLink
to="/messages"
- style={{ color: 'blue' }}
- activeStyle={{ color: 'green' }}
+ style={({ isActive }) => ({ color: isActive ? 'green' : 'blue' })}
>
Messages
</NavLink>
<NavLink
to="/messages"
- className="nav-link"
- activeClassName="activated"
+ className={({ isActive }) => "nav-link" + (isActive ? " activated" : "")}
>
Messages
</NavLink>
StaticRouter
from react-router-dom/server
链接:React Router | Upgrading from v5
useRouteMatch
withuseMatch
链接:React Router | Upgrading from v5
is not currently supported链接:[React Router | Upgrading from v5](