Skip to article frontmatterSkip to article content
Site not loading correctly?

This may be due to an incorrect BASE_URL configuration. See the MyST Documentation for reference.

延迟数据加载 (Deferred Data Loading)

TanStack Router 的设计初衷是并行运行加载器(Loaders),并在渲染下一个路由之前等待所有加载器解析完成。大多数情况下这很棒,但有时,你可能希望在后台加载剩余数据的同时,先给用户展示一些内容。

延迟数据加载是一种允许路由器渲染下一位置的“关键数据/标记”,同时在后台解析较慢的“非关键路由数据”的模式。这一过程在客户端和服务器端(通过流式传输)均可工作,是提升应用程序“感知性能”的绝佳方式。


使用 Await 实现延迟加载

要延迟加载缓慢或非关键的数据,只需在加载器响应中返回一个**未等待(unawaited)/未解析(unresolved)**的 Promise 即可:

// src/routes/posts.$postId.tsx
import { createFileRoute } from "@tanstack/react-router";

export const Route = createFileRoute("/posts/$postId")({
  loader: async () => {
    // 获取一些较慢的数据,但不要 await 它
    const slowDataPromise = fetchSlowData();

    // 获取并 await 一些快速解析的数据
    const fastData = await fetchFastData();

    return {
      fastData,
      deferredSlowData: slowDataPromise,
    };
  },
});

一旦所有被 await 的 Promise 解析完成,下一个路由就会开始渲染,而延迟的 Promise 则继续在后台解析。

在组件中,可以使用 Await 组件来处理这些延迟的 Promise:

// src/routes/posts.$postId.tsx
import { createFileRoute, Await } from "@tanstack/react-router";

export const Route = createFileRoute("/posts/$postId")({
  // ...
  component: PostIdComponent,
});

function PostIdComponent() {
  const { deferredSlowData, fastData } = Route.useLoaderData();

  // 处理 fastData...

  return (
    <div>
      <h1>{fastData.title}</h1>

      {/* 使用 Await 处理延迟数据 */}
      <Await promise={deferredSlowData} fallback={<div>加载中...</div>}>
        {(data) => {
          return <div>{data}</div>;
        }}
      </Await>
    </div>
  );
}

Await 组件通过触发最近的 Suspense 边界来解析 Promise。解析完成后,它会将结果传递给子函数进行渲染。


结合外部库进行延迟加载

当你依赖像 TanStack Query 这样的外部库进行数据获取时,延迟加载的工作方式有所不同,因为外部库在 TanStack Router 之外处理数据获取和缓存。

在这种情况下,你不需要使用 deferAwait,而是利用 loader 触发预取(Prefetch),然后使用库自带的 Hooks:

// src/routes/posts.$postId.tsx
export const Route = createFileRoute("/posts/$postId")({
  loader: async ({ context: { queryClient } }) => {
    // 触发慢速数据的获取,但不等待它
    queryClient.prefetchQuery(slowDataOptions());

    // 确保快速数据已获取并等待它
    await queryClient.ensureQueryData(fastDataOptions());
  },
});

组件内部写法:

function PostIdComponent() {
  // 快速数据直接通过 Suspense Query 获取
  const fastData = useSuspenseQuery(fastDataOptions());

  return (
    <div>
      <Suspense fallback={<div>加载中...</div>}>
        <SlowDataComponent />
      </Suspense>
    </div>
  );
}

function SlowDataComponent() {
  // 慢速数据在子组件中触发 Suspense
  const data = useSuspenseQuery(slowDataOptions());
  return <div>{data}</div>;
}

SSR 与流式传输延迟数据

流式传输需要服务器支持,并正确配置 TanStack Router。

请查阅 流式传输 SSR 指南 以了解详细步骤。

SSR 流式传输生命周期

以下是延迟数据流在 TanStack Router 中工作的简要流程: