升级到 React Router6 看这一篇就够了
我报名参加金石计划1期挑战——瓜分10万奖池,这是我的第1篇文章,点击查看活动详情
简介
React Router 是当前位置或 URL 的状态容器。它跟踪位置并在其更改时呈现不同的 。看了好多写的升级文章要么不全,要么是原文直译过来的,拗口不易理解,难受的不行,于是决定自己写。
一、前言
-
react Router6(以下简称V6),大量使用react hooks,因此,如果想升到V6,首先要保证react版本升到16.8及以上。
-
如果升到了V5.1,那可以平滑过渡到v6,那么[V5.1]主要有哪些变化?
二、升级
2.0 重磅!<Prompt>
目前V6[还不支持],升级请慎重!
个人觉得最重要的信息应该放在最前面,好多人按照官网步骤吭哧吭哧升级,到了最后一步,发现<Prompt/>
还不支持,那感觉就好像吃了一把苍蝇。
2.1、依赖
// 先移除掉之前的依赖 再安装
npm install react-router-dom
2.2、把所有<Switch>
元素更改为 <Routes>
-
改为
<Routes>
的主要原因:<Routes>
里面包裹的<Route>
和<Link>
的path支持相对路由,(/
开头的则是绝对路由,否则为相对路由)- 寻找路径基于最佳path匹配,而不是按顺序遍历,可避免路由不可达的错误
-
V5、V6 的路由嵌套demo 对比
// V5 写法
import {
BrowserRouter,
Switch,
Route,
Link,
useRouteMatch,
} from "react-router-dom";
function App() {
return (
<BrowserRouter>
<Switch>
<Route exact path="/">
<Home />
</Route>
<Route path="/users">
<Users />
</Route>
</Switch>
</BrowserRouter>
);
}
function Users() {
return (
<div>
<nav>
<Link to={`${match.url}/me`}>My Profile</Link>
</nav>
<Switch>
<Route path={`${match.path}/me`}>
<OwnUserProfile />
</Route>
<Route path={`${match.path}/:id`}>
<UserProfile />
</Route>
</Switch>
</div>
);
}
// V6 写法
import {
BrowserRouter,
Routes,
Route,
Link,
} from "react-router-dom";
// 版本1
function App() {
return (
<BrowserRouter>
// switch 改成 Routes
<Routes>
<Route path="/" element={<Home />} />
// 这里的 * 用来匹配子路由
<Route path="users/*" element={<Users />} />
</Routes>
</BrowserRouter>
);
}
// 子组件
function Users() {
return (
<div>
<nav>
<Link to="me">My Profile</Link>
</nav>
<Routes>
<Route path=":id" element={<UserProfile />} />
<Route path="me" element={<OwnUserProfile />} />
</Routes>
</div>
);
}
//版本2
import { Outlet} from "react-router-dom";
function App() {
return (
<BrowserRouter>
<Routes>
<Route path="/" element={<Home />} />
<Route path="users" element={<Users />}>
// children写法嵌套子路由
<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,用于表示当前所有子路由
<Outlet />
</div>
);
}
- 小结:
- 通过V5和V6版本1 对比可以看到,在V6中:
<Route path> and <Link to>
都是相对路径,不用再写父路由的 url或者path<Route exact>
exact 属性 不再需要,有子路由的时候使用*
- 通过 V5和V6版本2 对比可以看到:
- V5的
<Route children>
改为了<Route element>
- 当以
children
的写法写嵌套路由时(版本2),父组件里面写个<Outlet/>
即可自动匹配子路由的渲染
- V5的
- 通过V5和V6版本1 对比可以看到,在V6中:
tips
- V6的path比较简单。只支持
:
和*
,且*
只能放到path的末位,不能放中间- good:
<Route path={'abc*'}>
- bad:
<Route path={'a*bc'}
>
- good:
- 路由匹配的区分大小写开启
<Routes caseSensitive>
- v6 中的所有路径匹配都会忽略 URL 上的尾部斜杠。
<Link>
的path支持同级目录‘.’,和上级目录‘..’(可以当成cd 命令)
// 假如当前url是 /app/dashboard
// 写法如左,渲染如右
<Link to="stats"> => <a href="/app/dashboard/stats">
<Link to="../stats"> => <a href="/app/stats">
<Link to="../../stats"> => <a href="/stats">
<Link>
接受state的写法
import { Link } from "react-router-dom";
// V5
<Link to={{ pathname: "/home", state: state }} />
// V6
<Link to="/home" state={state} />
2.3 使用useRoutes 而非‘react-router-config’
- 配置写法在V5就已经支持了,在V6里大同小异
function App() {
let element = useRoutes([
{ path: "/", element: <Home /> },
{ path: "dashboard", element: <Dashboard /> },
{
path: "invoices",
element: <Invoices />,
children: [
{ path: ":id", element: <Invoice /> },
{ path: "sent", element: <SentInvoices /> },
],
},
{ path: "*", element: <NotFound /> },
]);
return element;
}
2.4 使用 useNavigate
替换 useHistory
// 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
function App() {
let navigate = useNavigate();
function handleClick() {
navigate("/home");
}
...
}
- 用法
- 使用navigate() 替换
history.push()
- navigate(to, { replace: true }) 替换
history.replace()
- 需要state,使用
navigate(to, { state })
- go、goBack、goForward,都改为navigate(number)的方式
- 使用navigate() 替换
// V5
import { useHistory } from "react-router-dom";
function App() {
const { go, goBack, goForward } = useHistory();
return (
<>
<button onClick={() => go(-2)}>
回退两页
</button>
<button onClick={goBack}>返回上一页</button>
<button onClick={goForward}>前进一页</button>
<button onClick={() => go(2)}>
前进两页
</button>
</>
);
}
// V6
import { useNavigate } from "react-router-dom";
function App() {
const navigate = useNavigate();
return (
<>
<button onClick={() => navigate(-2)}>
回退两页
</button>
<button onClick={() => navigate(-1)}>返回上一页</button>
<button onClick={() => navigate(1)}>
前进一页
</button>
<button onClick={() => navigate(2)}>
前进两页
</button>
</>
);
}
2.5 <Link >
上的 component属性 不再支持
2.6 NavLink 的变化
<NavLink exact>
属性名改为了<NavLink end>
- 移除了 activeStyle、activeClassName属性
2.7 StaticRouter 移到了一个新包里
// V5
import { StaticRouter } from "react-router-dom";
// V6
import { StaticRouter } from "react-router-dom/server";
2.8 用useMatch
替代 useRouteMatch
用法上也有了些变化,具体使用参考
- 接受一个pattern参数,可以是字符串或者对象
declare function useMatch<ParamKey extends string = string>(
pattern: PathPattern | string
): PathMatch<ParamKey> | null;
interface PathPattern {
path: string;
caseSensitive?: boolean;
end?: boolean;
}
- 参数为 pathPattern 对象时,参数名有变化
- useRouteMatch({ strict }) --> useMatch({ end })
- useRouteMatch({ sensitive }) -->useMatch({ caseSensitive })
2.9 matchPath的使用变化
- 第一个参数时 pathPattern对象,第二个 pathname
declare function matchPath<
ParamKey extends string = string
>(
pattern: PathPattern | string,
pathname: string
): PathMatch<ParamKey> | null;
- pathPattern 参数如上,不再支持
exact
和strict
,换成了end
和caseSensitive
;
// V5
// This is a React Router v5 app
import { matchPath } from "react-router-dom";
const match = matchPath("/users/123", {
path: "/users/:id",
exact: true, // Optional, defaults to false
strict: false, // Optional, defaults to false
});
// V6
import { matchPath } from "react-router-dom";
const match = matchPath(
{
path: "/users/:id",
caseSensitive: false, // Optional. Should be `true` if the static portions of the `path` should be matched in the same case.
end: true, // Optional. Should be `true` if this pattern should match the entire URL pathname
},
"/users/123"
);
完。
转载自:https://juejin.cn/post/7139053246730993701