TanStack Router 的设计是在并行运行所有 loader,并在所有 loader 解析后再渲染下一个路由。这在大多数情况下都很好,但偶尔,您可能希望在后台加载其余数据时更早地向用户显示一些内容。
延迟数据加载是一种模式,它允许路由器在后台解析较慢、非关键的路由数据时,渲染下一个路由位置的关键数据/标记。此过程可在客户端和服务器(通过流式传输)上进行,是提高应用程序感知性能的绝佳方式。
如果您正在使用 TanStack Query 或任何其他数据获取库,那么延迟数据加载的实现方式会略有不同。请跳至 使用外部库进行延迟数据加载 部分以获取更多信息。
要延迟加载缓慢或非关键数据,请在 loader 响应的任何位置返回一个**未等待/未解析**的 Promise
// src/routes/posts.$postId.tsx
import { createFileRoute, defer } from '@tanstack/solid-router'
export const Route = createFileRoute('/posts/$postId')({
loader: async () => {
// Fetch some slower data, but do not await it
const slowDataPromise = fetchSlowData()
// Fetch and await some data that resolves quickly
const fastData = await fetchFastData()
return {
fastData,
deferredSlowData: slowDataPromise,
}
},
})
// src/routes/posts.$postId.tsx
import { createFileRoute, defer } from '@tanstack/solid-router'
export const Route = createFileRoute('/posts/$postId')({
loader: async () => {
// Fetch some slower data, but do not await it
const slowDataPromise = fetchSlowData()
// Fetch and await some data that resolves quickly
const fastData = await fetchFastData()
return {
fastData,
deferredSlowData: slowDataPromise,
}
},
})
一旦任何已等待的 Promise 被解析,下一个路由将开始渲染,同时延迟的 Promise 会继续解析。
在组件中,可以使用 Await 组件来解析和使用延迟的 Promise。
// src/routes/posts.$postId.tsx
import { createFileRoute, Await } from '@tanstack/solid-router'
export const Route = createFileRoute('/posts/$postId')({
// ...
component: PostIdComponent,
})
function PostIdComponent() {
const { deferredSlowData, fastData } = Route.useLoaderData()
// do something with fastData
return (
<Await promise={deferredSlowData} fallback={<div>Loading...</div>}>
{(data) => {
return <div>{data}</div>
}}
</Await>
)
}
// src/routes/posts.$postId.tsx
import { createFileRoute, Await } from '@tanstack/solid-router'
export const Route = createFileRoute('/posts/$postId')({
// ...
component: PostIdComponent,
})
function PostIdComponent() {
const { deferredSlowData, fastData } = Route.useLoaderData()
// do something with fastData
return (
<Await promise={deferredSlowData} fallback={<div>Loading...</div>}>
{(data) => {
return <div>{data}</div>
}}
</Await>
)
}
提示
如果您的组件是代码分割的,您可以使用 getRouteApi 函数 来避免导入 Route 配置以获取对类型化的 useLoaderData() Hook 的访问。
Await
组件通过触发最近的 suspense 边界来解析 Promise,直到其被解析,然后它将以已解析的数据作为函数的 children 进行渲染。
如果 Promise 被拒绝,Await
组件将抛出序列化的错误,该错误可以被最近的错误边界捕获。
当您依赖 外部数据加载 和 TanStack Query 等外部库来获取路由信息时,延迟数据加载的实现方式会略有不同,因为库会在 TanStack Router 之外为您处理数据获取和缓存。
因此,您将需要使用路由的 loader 来启动数据获取,然后使用库的 Hooks 在组件中访问数据,而不是使用 defer 和 Await。
// src/routes/posts.$postId.tsx
import { createFileRoute } from '@tanstack/solid-router'
import { slowDataOptions, fastDataOptions } from '~/api/query-options'
export const Route = createFileRoute('/posts/$postId')({
loader: async ({ context: { queryClient } }) => {
// Kick off the fetching of some slower data, but do not await it
queryClient.prefetchQuery(slowDataOptions())
// Fetch and await some data that resolves quickly
await queryClient.ensureQueryData(fastDataOptions())
},
})
// src/routes/posts.$postId.tsx
import { createFileRoute } from '@tanstack/solid-router'
import { slowDataOptions, fastDataOptions } from '~/api/query-options'
export const Route = createFileRoute('/posts/$postId')({
loader: async ({ context: { queryClient } }) => {
// Kick off the fetching of some slower data, but do not await it
queryClient.prefetchQuery(slowDataOptions())
// Fetch and await some data that resolves quickly
await queryClient.ensureQueryData(fastDataOptions())
},
})
然后在您的组件中,您可以使用库的 Hooks 来访问数据
// src/routes/posts.$postId.tsx
import { createFileRoute } from '@tanstack/solid-router'
import { useSuspenseQuery } from '@tanstack/solid-query'
import { slowDataOptions, fastDataOptions } from '~/api/query-options'
export const Route = createFileRoute('/posts/$postId')({
// ...
component: PostIdComponent,
})
function PostIdComponent() {
const fastData = useSuspenseQuery(fastDataOptions())
// do something with fastData
return (
<Suspense fallback={<div>Loading...</div>}>
<SlowDataComponent />
</Suspense>
)
}
function SlowDataComponent() {
const data = useSuspenseQuery(slowDataOptions())
return <div>{data}</div>
}
// src/routes/posts.$postId.tsx
import { createFileRoute } from '@tanstack/solid-router'
import { useSuspenseQuery } from '@tanstack/solid-query'
import { slowDataOptions, fastDataOptions } from '~/api/query-options'
export const Route = createFileRoute('/posts/$postId')({
// ...
component: PostIdComponent,
})
function PostIdComponent() {
const fastData = useSuspenseQuery(fastDataOptions())
// do something with fastData
return (
<Suspense fallback={<div>Loading...</div>}>
<SlowDataComponent />
</Suspense>
)
}
function SlowDataComponent() {
const data = useSuspenseQuery(slowDataOptions())
return <div>{data}</div>
}
流式传输的 Promise 遵循与其关联的 loader 数据相同的生命周期。它们甚至可以被预加载!
您的每周 JavaScript 资讯。每周一免费发送给超过 10 万开发者。