Route Modules.

58 阅读4分钟

在 routes.ts 中引用的文件被称为路由模块。

route("teams/:teamId", "./team.tsx"),
//           路由模块  ^^^^^^^^

路由模块是 React Router 框架功能的基础,它们定义了以下内容:

  • 自动代码拆分

  • 数据加载

  • 操作

  • 重新验证

  • 错误边界

  • 以及更多功能

本指南是对每个路由模块功能的快速概述,后续的入门指南将会更详细地介绍这些功能。

组件(默认)

定义了当路由匹配时要渲染的组件,示例如下:

export default function MyRouteComponent() {
  return (
    <div>
      <h1>看呀!</h1>
      <p>
        大约10年过去了,我还在使用React Router呢。
      </p>
    </div>
  );
}

加载器(loader)

路由加载器会在路由组件渲染之前向其提供数据。它们仅在服务器端渲染时在服务器上被调用,或者在构建过程中进行预渲染时被调用,示例如下:

export async function loader() {
  return { message: "Hello, world!" };
}

export default function MyRoute({ loaderData }) {
  return <h1>{loaderData.message}</h1>;
}

可参考:加载器参数客户端加载器(clientLoader)

客户端加载器(clientLoader)

仅在浏览器中被调用,路由客户端加载器除了向路由组件提供由路由加载器提供的数据之外,还可以额外提供数据,或者替代路由加载器来提供数据,示例如下:

export async function clientLoader({ serverLoader }) {
  // 调用服务器端加载器
  const serverData = await serverLoader();
  // 并且/或者在客户端获取数据
  const data = getDataFromClient();
  // 返回数据,以便通过 useLoaderData() 暴露出来
  return data;
}

客户端加载器可以通过在函数上设置 hydrate 属性来参与服务器渲染页面的初始页面加载水合过程,示例如下:

export async function clientLoader() {
  //...
}
clientLoader.hydrate = true as const;

通过使用 as const,TypeScript 会推断出 clientLoader.hydrate 的类型为 true 而不是 boolean,这样 React Router 就能根据 clientLoader.hydrate 的值推导出 loaderData 的类型了。

操作(action)

路由操作允许在服务器端进行数据变更,并且当从 <Form>useFetcher 和 useSubmit 调用时,会自动重新验证页面上所有加载器的数据,示例如下:

// route("/list", "./list.tsx")
import { Form } from "react-router";
import { TodoList } from "~/components/TodoList";

// 这些数据将在操作完成后被加载...
export async function loader() {
  const items = await fakeDb.getItems();
  return { items };
}

//...以便这里的列表能自动更新
export default function Items({ loaderData }) {
  return (
    <div>
      <List items={loaderData.items} />
      <Form method="post" navigate={false} action="/list">
        <input type="text" name="title" />
        <button type="submit">Create Todo</button>
      </Form>
    </div>
  );
}

export async function action({ request }) {
  const data = await request.formData();
  const todo = await fakeDb.addItem({
    title: data.get("title"),
  });
  return { ok: true };
}

客户端操作(clientAction)

与路由操作类似,但仅在浏览器中被调用,示例如下:

export async function clientAction({ serverAction }) {
  fakeInvalidateClientSideCache();
  // 如果需要,仍然可以调用服务器端操作
  const data = await serverAction();
  return data;
}

错误边界(ErrorBoundary)

当其他路由模块的 API 抛出异常时,路由模块的错误边界将会进行渲染,而不是渲染路由组件,示例如下:

import {
  isRouteErrorResponse,
  useRouteError,
} from "react-router";

export function ErrorBoundary() {
  const error = useRouteError();

  if (isRouteErrorResponse(error)) {
    return (
      <div>
        <h1>
          {error.status} {error.statusText}
        </h1>
        <p>{error.data}</p>
      </div>
    );
  } else if (error instanceof Error) {
    return (
      <div>
        <h1>Error</h1>
        <p>{error.message}</p>
        <p>堆栈跟踪信息如下:</p>
        <pre>{error.stack}</pre>
      </div>
    );
  } else {
    return <h1>Unknown Error</h1>;
  }
}

水合回退(HydrateFallback)

在初始页面加载时,路由组件只有在客户端加载器完成工作后才会渲染。如果导出了 HydrateFallback,它可以立即渲染来替代路由组件,示例如下:

export async function clientLoader() {
  const data = await fakeLoadLocalGameData();
  return data;
}

export function HydrateFallback() {
  return <p>正在加载游戏...</p>;
}

export default function Component({ loaderData }) {
  return <Game data={loaderData} />;
}

头部信息(headers)

路由头部信息定义了在服务器渲染时要随响应发送的 HTTP 头部,示例如下:

export function headers() {
  return {
    "X-Stretchy-Pants": "its for fun",
    "Cache-Control": "max-age=300, s-maxage=3600",
  };
}

句柄(handle)

路由句柄允许应用程序向 useMatches 中的路由匹配添加任何内容,以创建抽象内容(比如面包屑等),示例如下:

export const handle = {
  its: "all yours",
};

链接(links)

路由链接定义了要在文档 <head> 中渲染的 <link> 元素,示例如下:

export function links() {
  return [
    {
      rel: "icon",
      href: "/favicon.png",
      type: "image/png",
    },
    {
      rel: "stylesheet",
      href: "https://example.com/some/styles.css",
    },
    {
      rel: "preload",
      href: "/images/banner.jpg",
      as: "image",
    },
  ];
}

所有路由的链接都会被汇总,并通过 <Links /> 组件进行渲染,通常在应用程序的根组件中进行渲染,示例如下:

import { Links } from "react-router";

export default function Root() {
  return (
    <html>
      <head>
        <Links />
      </head>
      <body />
    </html>
  );
}

元数据(meta)

路由元数据定义了要在文档 <head> 中渲染的元标签,示例如下:

export function meta() {
  return [
    { title: "非常酷的应用" },
    {
      property: "og:title",
      content: "非常酷的应用",
    },
    {
      name: "description",
      content: "这个应用是最棒的",
    },
  ];
}

所有路由的元数据都会被汇总,并通过 <Meta /> 组件进行渲染,通常在应用程序的根组件中进行渲染,示例如下:

import { Meta } from "react-router";

export default function Root() {
  return (
    <html>
      <head>
        <Meta />
      </head>
      <body />
    </html>
  );
}

应重新验证(shouldRevalidate)

默认情况下,所有路由在操作后都会重新验证。该函数允许路由针对那些不影响其数据的操作选择不进行重新验证,示例如下:

import type { ShouldRevalidateFunctionArgs } from "react-router";

export function shouldRevalidate(
  arg: ShouldRevalidateFunctionArgs
) {
  return true;
}