一文了解React Router6.4新特性

641 阅读5分钟

在去年的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:

createBrowserRouter:

官方推荐使用它来创建客户端路由,它使用DOM History API来更新 URL 并管理历史堆栈。它还支持 v6.4 数据 API,如loaderactionfetchers等。

类型声明:

function createBrowserRouter(
  routes: RouteObject[],
  opts?: {
    basename?: string;
    window?: Window;
  }
): RemixRouter;

参数说明:

createHashRouter:

hash路由模式

createMemoryRouter:

内存路由器不使用浏览器的历史记录,而是在内存中管理它自己的历史堆栈。它主要用于测试和组件开发工具

RouterProvider:

提供路由配置并作为路由渲染入口

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官网进行深入学习。