React router之数据加载

205 阅读1分钟

数据通过 loader 和 clientLoader 提供给路由组件。

加载器(loader)数据会自动从加载器中进行序列化,并在组件中进行反序列化。除了字符串、数字这类基本值之外,加载器还可以返回 PromiseMapSetDate 等类型的数据。

客户端数据加载
clientLoader 用于在客户端获取数据。对于那些你更倾向于只通过浏览器来获取数据的页面或整个项目来说,这很有用处。

// route("products/:pid", "./product.tsx");
import type { Route } from "./+types/product";

export async function clientLoader({
  params,
}: Route.ClientLoaderArgs) {
  const res = await fetch(`/api/products/${params.pid}`);
  const product = await res.json();
  return product;
}

export default function Product({
  loaderData,
}: Route.ComponentProps) {
  const { name, description } = loaderData;
  return (
    <div>
      <h1>{name}</h1>
      <p>{description}</p>
    </div>
  );
}

服务器端数据加载
在进行服务器端渲染时,loader 既用于初始页面加载,也用于客户端导航。客户端导航时,React Router 会自动从浏览器向服务器发起获取操作来调用加载器。

// route("products/:pid", "./product.tsx");
import type { Route } from "./+types/product";
import { fakeDb } from "../db";

export async function loader({ params }: Route.LoaderArgs) {
  const product = await fakeDb.getProduct(params.pid);
  return product;
}

export default function Product({
  loaderData,
}: Route.ComponentProps) {
  const { name, description } = loaderData;
  return (
    <div>
      <h1>{name}</h1>
      <p>{description}</p>
    </div>
  );
}

请注意,加载器函数会从客户端代码包中移除,这样你就可以使用仅服务器端可用的 API,而不用担心它们被包含在浏览器端的代码中。

静态数据加载
在进行预渲染时,加载器用于在生产构建期间获取数据。

// route("products/:pid", "./product.tsx");
import type { Route } from "./+types/product";

export async function loader({ params }: Route.LoaderArgs) {
  let product = await getProductFromCSVFile(params.pid);
  return product;
}

export default function Product({
  loaderData,
}: Route.ComponentProps) {
  const { name, description } = loaderData;
  return (
    <div>
      <h1>{name}</h1>
      <p>{description}</p>
    </div>
  );
}

要预渲染的 URL 在 react-router.config.ts 文件中指定:

import type { Config } from "@react-router/dev/config";

export default {
  async prerender() {
    let products = await readProductsFromCSVFile();
    return products.map(
      (product) => `/products/${product.id}`
    );
  },
} satisfies Config;

请注意,在进行服务器端渲染时,任何未被预渲染的 URL 都会像往常一样进行服务器端渲染,这使得你可以对单个路由中的部分数据进行预渲染,同时对其余部分仍进行服务器端渲染。

同时使用两种加载器
loader 和 clientLoader 可以一起使用。loader 会在服务器端用于初始的服务器端渲染(或预渲染),而 clientLoader 则会在后续的客户端导航中使用。

// route("products/:pid", "./product.tsx");
import type { Route } from "./+types/product";
import { fakeDb } from "../db";

export async function loader({ params }: Route.LoaderArgs) {
  return fakeDb.getProduct(params.pid);
}

export async function clientLoader({
  params,
}: Route.ClientLoader) {
  const res = await fetch(`/api/products/${params.pid}`);
  return res.json();
}

export default function Product({
  loaderData,
}: Route.ComponentProps) {
  const { name, description } = loaderData;

  return (
    <div>
      <h1>{name}</h1>
      <p>{description}</p>
    </div>
  );
}