在去年的11月份,react-router发布了新的版本6.0.0,之前一直没有深入的了解具体的更新变化,最近关注到又发布了更新,2022年的9月14日发布了6.4.0版本,6.4版本中新增了大量的组件和API,使用方式也发生了一些变化,最近几天又时间就进行了一番学习了解,在此分享给有需要的前端同学。
首先直观的看下如何使用:
import React from "react";
import { createRoot } from "react-dom/client";
import {
createBrowserRouter,
RouterProvider,
Route,
Link,
} from "react-router-dom";
const router = createBrowserRouter([
{
path: "/",
element: (
<div>
<h1>Hello World</h1>
<Link to="about">About Us</Link>
</div>
),
},
{
path: "about",
element: <div>About</div>,
},
]);
createRoot(document.getElementById("root")).render(
<RouterProvider router={router} />
);
再来看下5.x的使用方式
import React from 'react';
import ReactDOM from 'react-dom';
import {
BrowserRouter as Router,
Switch,
Route,
Link
} from "react-router-dom";
function App(){
return (
<Router>
<Switch>
<Route exact path="/">
<Home />
</Route>
<Route path="/about">
<About />
</Route>
<Route path="/dashboard">
<Dashboard />
</Route>
</Switch>
</Router>
)
}
ReactDOM.render(<App></App>, document.getElementById('root'))
可以看到,在使用方式上,6与5还是有比较大的变动。
一、新特性
1.数据路由器
6.4版本中提供了3个新的创建路由的API:
官方推荐使用它来创建客户端路由,它使用DOM History API来更新 URL 并管理历史堆栈。它还支持 v6.4 数据 API,如loader、action、fetchers等。
类型声明:
function createBrowserRouter(
routes: RouteObject[],
opts?: {
basename?: string;
window?: Window;
}
): RemixRouter;
参数说明:
- basename: 创建路由的基础路径,如basename:'/app',则根路径即为:http://localhost:3000/app
hash路由模式
内存路由器不使用浏览器的历史记录,而是在内存中管理它自己的历史堆栈。它主要用于测试和组件开发工具
提供路由配置并作为路由渲染入口
2. Route
Route是路由中非常重要的一个组件,提供将url片段映射到组件,在之前的版本中,react-router只能以jsx的形式提供路由配置,如:
<Switch>
<Route exact path="/">
<Home />
</Route>
<Route path="/about">
<About />
</Route>
<Route path="/dashboard">
<Dashboard />
</Route>
</Switch>
在最新的6.4版本中,react-router提供了2种路由配置模式,jsx和object,object配置形式如下:
const router = createBrowserRouter([
{
path: "/",
element: (
<div>
<h1>Hello World</h1>
<Link to="about">About Us</Link>
</div>
),
},
{
path: "about",
element: <div>About</div>,
},
]);
然而如果想继续使用jsx的配置形式,需要通过使用createRoutesFromElementsAPI
createBrowserRouter(
createRoutesFromElements(
<Route path="/" element={<Root />}>
<Route path="contact" element={<Contact />} />
<Route
path="dashboard"
element={<Dashboard />}
loader={fetch("/api/dashboard.json", {
signal: request.signal,
})}
/>
<Route element={<AuthLayout />}>
<Route
path="login"
element={<Login />}
loader={redirectIfUser}
/>
<Route path="logout" />
</Route>
</Route>
)
);
Route类型声明:
interface RouteObject {
path?: string;
index?: boolean;
children?: React.ReactNode;
caseSensitive?: boolean;
id?: string;
loader?: LoaderFunction;
action?: ActionFunction;
element?: React.ReactNode | null;
errorElement?: React.ReactNode | null;
handle?: RouteObject["handle"];
shouldRevalidate?: ShouldRevalidateFunction;
}
参数说明:
- path: url片段
- index: 索引路由,多用于嵌套路由未匹配时设置默认路由。
- children: 子路由嵌套
- caseSensitive:path是否匹配大小写,默认不区分大小写
- loader: 也可以称为vue-router中的路由钩子函数,在加载匹配路由前执行,可以作为前置数据拉取、权限相关操作。
- action:与提供Form组件使用,拦截Form默认行为并获取表单提交数据
- element:url片段匹配组件元素,注意,6版本中使用的是元素并不是之前版本中的component和render
- errorElement: 错误路由处理,在路由渲染错误时,如loader,action等发生错误。如果路由没有errorElement,则错误将冒泡到最近的带有 的父路由errorElement。
3. 组件
Form
Form 组件是一个普通 HTML表单的包装器,可以拦截变表单动作url跳转。
Await
Await组件通过自动错误处理,延迟呈现依赖延迟数据的组件部分;
此应用场景通常在使用了Route loaderFunction来提前加载匹配组件元素的数据,如果数据获取时间太长,则路由将要等待数据准备完毕才可以显示。通过使用Await组件,则可以让匹配路由提前显示。
例:
import { defer, useLoaderData } from "react-router-dom";
import { getPackageLocation } from "./api/packages";
// loader前置数据
async function loader({ params }) {
const packageLocationPromise = getPackageLocation(
params.packageId
);
return defer({
packageLocation: packageLocationPromise,
});
}
// 匹配元素
export default function PackageRoute() {
const data = useLoaderData();
return (
<main>
// 此部分无需等待直接显示
<h1>Let's locate your package</h1>
// React.Suspense
<React.Suspense
fallback={<p>Loading package location...</p>}
>
<Await
resolve={data.packageLocation}
errorElement={
<p>Error loading package location!</p>
}
>
// 数据加载成功之后显示
{(packageLocation) => (
<p>
Your package is at {packageLocation.latitude}{" "}
lat and {packageLocation.longitude} long.
</p>
)}
</Await>
</React.Suspense>
</main>
);
}
ScrollRestoration
该组件将在加载器完成后模拟浏览器在位置更改时的滚动恢复,以确保滚动位置恢复到正确的位置
4.Hooks
useActionData: 与Form组件配合使用,获取Form数据
import { useActionData } from "react-router-dom";
function SomeComponent() {
let actionData = useActionData();
// ...
}
useAsyncValue: 获取await组件延迟数据
useLoaderData: 获取Route指定loaderFunction的返回值
import {
createBrowserRouter,
RouterProvider,
useLoaderData,
} from "react-router-dom";
// loader函数
function loader() {
// 返回值
return fetchFakeAlbums();
}
export function Albums() {
// 组件获取loaderFunction返回值
const albums = useLoaderData();
// ...
}
const router = createBrowserRouter([
{
path: "/",
// 这里loader
loader: loader,
element: <Albums />,
},
]);
ReactDOM.createRoot(el).render(
<RouterProvider router={router} />
);
useMatches: 返回匹配的路由值为一数组 useNavigation:获取当前匹配路由信息
import { useNavigation } from "react-router-dom";
function SomeComponent() {
const navigation = useNavigation();
navigation.state;
navigation.location;
navigation.formData;
navigation.formAction;
navigation.formMethod;
}
useRouteError: 获取路由渲染错误信息,如loader,action等错误信息
function ErrorBoundary() {
const error = useRouteError();
console.error(error);
return <div>{error.message}</div>;
}
<Route
errorElement={<ErrorBoundary />}
loader={() => {
// unexpected errors in loaders/actions
something.that.breaks();
}}
action={() => {
// stuff you throw on purpose in loaders/actions
throw new Response("Bad Request", { status: 400 });
}}
element={
// and errors thrown while rendering
<div>{breaks.while.rendering}</div>
}
/>;
useRouteLoaderData: 可以使得任何当前渲染的路由获取深层次的路由树数据,参数为路由配置指定的id
// create router
createBrowserRouter([
{
path: "/",
loader: () => fetchUser(),
element: <Root />,
// 指定id
id: "root",
children: [
{
path: "jobs/:jobId",
loader: loadJob,
element: <JobListing />,
},
],
},
]);
在渲染的组件中
import { useRouteLoaderData } from "react-router-dom";
function SomeComp() {
// 获取指定id的loader data
const user = useRouteLoaderData("root");
// ...
}
总结
以上分享了最新的react-router6.4.0版本的新特性,可以看到这个版本的更新变化非常大,首次提供了基于object的方式描述路由结构,首次提供了路由钩子函数loader,也提供了更多的hook函数。感兴趣的前端同学可以打开 react-router官网进行深入学习。