框架
版本

预加载

TanStack Router 中的预加载是一种在用户实际导航到某个路由之前加载该路由的方式。这对于用户接下来可能访问的路由非常有用。例如,如果你有一个帖子列表,用户很可能会点击其中一个,你可以预加载该帖子路由,以便用户点击时即可使用。

支持的预加载策略

  • 意图(Intent)
    • 通过“意图”进行预加载,是通过在 <Link> 组件上使用悬停和触摸开始事件,来预加载目标路由的依赖项。
    • 此策略适用于预加载用户接下来可能访问的路由。
  • 视口可见性(Viewport Visibility)
    • 通过“视口”进行预加载,是通过使用 Intersection Observer API,当 <Link> 组件在视口中时,预加载目标路由的依赖项。
    • 此策略适用于预加载在首屏下方或屏幕外的路由。
  • 渲染(Render)
    • 通过“渲染”进行预加载,是在 <Link> 组件在 DOM 中渲染后立即预加载目标路由的依赖项。
    • 此策略适用于预加载始终需要的路由。

预加载的数据在内存中保留多久?

预加载的路由匹配项会临时缓存在内存中,但有几个重要的注意事项:

  • 默认情况下,未使用的预加载数据会在 30 秒后被移除。这可以通过在路由器上设置 defaultPreloadMaxAge 选项来配置。
  • 显然,当路由被加载时,其预加载版本会被提升为路由器的正常待处理匹配状态。

如果您需要对预加载数据的预加载、缓存和/或垃圾回收进行更多控制,您应该使用像 TanStack Query 这样的外部缓存库。

预加载路由最简单的方法是为整个路由器设置 defaultPreload 选项为 intent

tsx
import { createRouter } from '@tanstack/react-router'

const router = createRouter({
  // ...
  defaultPreload: 'intent',
})
import { createRouter } from '@tanstack/react-router'

const router = createRouter({
  // ...
  defaultPreload: 'intent',
})

这将默认开启应用程序中所有 <Link> 组件的 intent 预加载。您也可以在单个 <Link> 组件上设置 preload 属性来覆盖默认行为。

预加载延迟

默认情况下,预加载会在用户悬停或触摸 <Link> 组件 50ms 后开始。您可以通过在路由器上设置 defaultPreloadDelay 选项来更改此延迟。

tsx
import { createRouter } from '@tanstack/react-router'

const router = createRouter({
  // ...
  defaultPreloadDelay: 100,
})
import { createRouter } from '@tanstack/react-router'

const router = createRouter({
  // ...
  defaultPreloadDelay: 100,
})

您也可以在单个 <Link> 组件上设置 preloadDelay 属性,以针对每个链接覆盖默认行为。

内置预加载 & preloadStaleTime

如果您正在使用内置加载器,可以通过设置 routerOptions.defaultPreloadStaleTimerouteOptions.preloadStaleTime 为毫秒数,来控制预加载数据被视为新鲜的时间,直到触发另一次预加载。默认情况下,预加载数据被视为新鲜 30 秒。

要更改此设置,您可以在路由器上设置 defaultPreloadStaleTime 选项

tsx
import { createRouter } from '@tanstack/react-router'

const router = createRouter({
  // ...
  defaultPreloadStaleTime: 10_000,
})
import { createRouter } from '@tanstack/react-router'

const router = createRouter({
  // ...
  defaultPreloadStaleTime: 10_000,
})

或者,您可以在单个路由上使用 routeOptions.preloadStaleTime 选项

tsx
// src/routes/posts.$postId.tsx
export const Route = createFileRoute('/posts/$postId')({
  loader: async ({ params }) => fetchPost(params.postId),
  // Preload the route again if the preload cache is older than 10 seconds
  preloadStaleTime: 10_000,
})
// src/routes/posts.$postId.tsx
export const Route = createFileRoute('/posts/$postId')({
  loader: async ({ params }) => fetchPost(params.postId),
  // Preload the route again if the preload cache is older than 10 seconds
  preloadStaleTime: 10_000,
})

使用外部库进行预加载

当集成像 React Query 这样的外部缓存库时,这些库有自己确定数据是否过期的机制,您可能希望覆盖 TanStack Router 默认的预加载和过时重新验证逻辑。这些库通常使用像 staleTime 这样的选项来控制数据的新鲜度。

为了在 TanStack Router 中自定义预加载行为并充分利用外部库的缓存策略,您可以通过将 routerOptions.defaultPreloadStaleTime 或 routeOptions.preloadStaleTime 设置为 0 来绕过内置缓存。这确保所有预加载在内部都被标记为过期,并且加载器总是被调用,从而允许您的外部库(如 React Query)管理数据加载和缓存。

例如

tsx
import { createRouter } from '@tanstack/react-router'

const router = createRouter({
  // ...
  defaultPreloadStaleTime: 0,
})
import { createRouter } from '@tanstack/react-router'

const router = createRouter({
  // ...
  defaultPreloadStaleTime: 0,
})

这样一来,您就可以使用诸如 React Query 的 staleTime 等选项来控制预加载数据的新鲜度。

手动预加载

如果您需要手动预加载路由,可以使用路由器的 preloadRoute 方法。它接受一个标准的 TanStack NavigateOptions 对象,并返回一个 Promise,该 Promise 在路由预加载完成后解析。

tsx
function Component() {
  const router = useRouter()

  useEffect(() => {
    async function preload() {
      try {
        const matches = await router.preloadRoute({
          to: postRoute,
          params: { id: 1 },
        })
      } catch (err) {
        // Failed to preload route
      }
    }

    preload()
  }, [router])

  return <div />
}
function Component() {
  const router = useRouter()

  useEffect(() => {
    async function preload() {
      try {
        const matches = await router.preloadRoute({
          to: postRoute,
          params: { id: 1 },
        })
      } catch (err) {
        // Failed to preload route
      }
    }

    preload()
  }, [router])

  return <div />
}

如果您只需要预加载路由的 JS 块,可以使用路由器的 loadRouteChunk 方法。它接受一个路由对象,并返回一个 Promise,该 Promise 在路由块加载完成后解析。

tsx
function Component() {
  const router = useRouter()

  useEffect(() => {
    async function preloadRouteChunks() {
      try {
        const postsRoute = router.routesByPath['/posts']
        await Promise.all([
          router.loadRouteChunk(router.routesByPath['/']),
          router.loadRouteChunk(postsRoute),
          router.loadRouteChunk(postsRoute.parentRoute),
        ])
      } catch (err) {
        // Failed to preload route chunk
      }
    }

    preloadRouteChunks()
  }, [router])

  return <div />
}
function Component() {
  const router = useRouter()

  useEffect(() => {
    async function preloadRouteChunks() {
      try {
        const postsRoute = router.routesByPath['/posts']
        await Promise.all([
          router.loadRouteChunk(router.routesByPath['/']),
          router.loadRouteChunk(postsRoute),
          router.loadRouteChunk(postsRoute.parentRoute),
        ])
      } catch (err) {
        // Failed to preload route chunk
      }
    }

    preloadRouteChunks()
  }, [router])

  return <div />
}
我们的合作伙伴
Code Rabbit
Netlify
Neon
Clerk
Convex
Sentry
订阅 Bytes

您的每周 JavaScript 资讯。每周一免费发送给超过 10 万开发者。

Bytes

无垃圾邮件。您可以随时取消订阅。

订阅 Bytes

您的每周 JavaScript 资讯。每周一免费发送给超过 10 万开发者。

Bytes

无垃圾邮件。您可以随时取消订阅。