代码分割和懒加载是提高应用程序 bundle 大小和加载性能的强大技术。
TanStack Router 将代码分为两类
关键路由配置 - 渲染当前路由并尽早启动数据加载过程所需的代码。
非关键/懒加载路由配置 - 不是匹配路由所必需的代码,可以按需加载。
🧠 为什么加载器不分割?
加载器已经是一个异步边界,因此您需要支付双倍的代价来获取 chunk 并 等待加载器执行。
从类别上讲,它比组件更不可能导致 bundle 大小过大。
加载器是路由最重要的可预加载资源之一,特别是如果您使用默认的预加载意图(例如悬停在链接上),因此加载器必须可用,而无需任何额外的异步开销。
了解分割加载器的缺点后,如果您仍然想继续,请前往 数据加载器分割 部分。
由于 TanStack Router 基于文件的路由系统旨在支持平面和嵌套文件结构,因此可以将路由文件封装到单个目录中,而无需任何额外的配置。
要将路由文件封装到目录中,请将路由文件本身移动到名为 .route 的文件中,该文件位于与路由文件同名的目录中。
例如,如果您有一个名为 posts.tsx 的路由文件,您将创建一个名为 posts 的新目录,并将 posts.tsx 文件移动到该目录中,并将其重命名为 route.tsx。
之前
之后
TanStack Router 支持多种代码分割方法。如果您正在使用基于代码的路由,请跳至 基于代码的分割 部分。
当您使用基于文件的路由时,可以使用以下方法进行代码分割
这是代码分割路由文件最简单且最强大的方法。
当使用 autoCodeSplitting 功能时,TanStack Router 将根据上面提到的非关键路由配置自动代码分割您的路由文件。
重要提示
自动代码分割功能仅在使用基于文件的路由和我们的 支持的打包器 之一时可用。如果您仅使用 CLI (@tanstack/router-cli),则不会工作。
要启用自动代码分割,您只需要将以下内容添加到 TanStack Router 打包器插件的配置中
// vite.config.ts
import { defineConfig } from 'vite'
import react from '@vitejs/plugin-react'
import { TanStackRouterVite } from '@tanstack/router-plugin/vite'
export default defineConfig({
plugins: [
TanStackRouterVite({
// ...
autoCodeSplitting: true,
}),
react(), // Make sure to add this plugin after the TanStack Router Bundler plugin
],
})
// vite.config.ts
import { defineConfig } from 'vite'
import react from '@vitejs/plugin-react'
import { TanStackRouterVite } from '@tanstack/router-plugin/vite'
export default defineConfig({
plugins: [
TanStackRouterVite({
// ...
autoCodeSplitting: true,
}),
react(), // Make sure to add this plugin after the TanStack Router Bundler plugin
],
})
就是这样!TanStack Router 将根据其关键和非关键路由配置自动代码分割您的所有路由文件。
如果您想更精细地控制代码分割过程,请前往 自动代码分割 指南,以了解更多可用选项。
如果您无法使用自动代码分割功能,您仍然可以使用 .lazy.tsx 后缀来代码分割您的路由文件。它就像将您的代码移动到带有 .lazy.tsx 后缀的单独文件中一样简单,并使用 createLazyFileRoute 函数而不是 createFileRoute。
重要提示
使用 createRootRoute 或 createRootRouteWithContext 的 __root.tsx 路由文件不支持代码分割,因为它始终在渲染,无论当前路由如何。
这些是 createLazyFileRoute 支持的唯一选项
导出名称 | 描述 |
---|---|
component | 路由要渲染的组件。 |
errorComponent | 加载路由时发生错误时要渲染的组件。 |
pendingComponent | 路由加载时要渲染的组件。 |
notFoundComponent | 如果抛出未找到错误,则要渲染的组件。 |
当您使用 .lazy.tsx 时,您可以将路由拆分为两个文件以启用代码分割
之前 (单个文件)
// src/routes/posts.tsx
import { createFileRoute } from '@tanstack/solid-router'
import { fetchPosts } from './api'
export const Route = createFileRoute('/posts')({
loader: fetchPosts,
component: Posts,
})
function Posts() {
// ...
}
// src/routes/posts.tsx
import { createFileRoute } from '@tanstack/solid-router'
import { fetchPosts } from './api'
export const Route = createFileRoute('/posts')({
loader: fetchPosts,
component: Posts,
})
function Posts() {
// ...
}
之后 (拆分为两个文件)
此文件将包含关键路由配置
// src/routes/posts.tsx
import { createFileRoute } from '@tanstack/solid-router'
import { fetchPosts } from './api'
export const Route = createFileRoute('/posts')({
loader: fetchPosts,
})
// src/routes/posts.tsx
import { createFileRoute } from '@tanstack/solid-router'
import { fetchPosts } from './api'
export const Route = createFileRoute('/posts')({
loader: fetchPosts,
})
非关键路由配置将进入带有 .lazy.tsx 后缀的文件中
// src/routes/posts.lazy.tsx
import { createLazyFileRoute } from '@tanstack/solid-router'
export const Route = createLazyFileRoute('/posts')({
component: Posts,
})
function Posts() {
// ...
}
// src/routes/posts.lazy.tsx
import { createLazyFileRoute } from '@tanstack/solid-router'
export const Route = createLazyFileRoute('/posts')({
component: Posts,
})
function Posts() {
// ...
}
您可能会遇到一种情况,最终从路由文件中拆分出所有内容,使其为空!在这种情况下,只需完全删除路由文件!将自动为您生成一个虚拟路由,作为代码分割文件的锚点。此虚拟路由将直接存在于生成的路由树文件中。
之前 (虚拟路由)
// src/routes/posts.tsx
import { createFileRoute } from '@tanstack/solid-router'
export const Route = createFileRoute('/posts')({
// Hello?
})
// src/routes/posts.tsx
import { createFileRoute } from '@tanstack/solid-router'
export const Route = createFileRoute('/posts')({
// Hello?
})
// src/routes/posts.lazy.tsx
import { createLazyFileRoute } from '@tanstack/solid-router'
export const Route = createLazyFileRoute('/posts')({
component: Posts,
})
function Posts() {
// ...
}
// src/routes/posts.lazy.tsx
import { createLazyFileRoute } from '@tanstack/solid-router'
export const Route = createLazyFileRoute('/posts')({
component: Posts,
})
function Posts() {
// ...
}
之后 (虚拟路由)
// src/routes/posts.lazy.tsx
import { createLazyFileRoute } from '@tanstack/solid-router'
export const Route = createLazyFileRoute('/posts')({
component: Posts,
})
function Posts() {
// ...
}
// src/routes/posts.lazy.tsx
import { createLazyFileRoute } from '@tanstack/solid-router'
export const Route = createLazyFileRoute('/posts')({
component: Posts,
})
function Posts() {
// ...
}
哒哒!🎉
如果您正在使用基于代码的路由,您仍然可以使用 Route.lazy() 方法和 createLazyRoute 函数来代码分割您的路由。您需要将路由配置拆分为两个部分
使用 createLazyRoute 函数创建一个懒加载路由。
// src/posts.tsx
export const Route = createLazyRoute('/posts')({
component: MyComponent,
})
function MyComponent() {
return <div>My Component</div>
}
// src/posts.tsx
export const Route = createLazyRoute('/posts')({
component: MyComponent,
})
function MyComponent() {
return <div>My Component</div>
}
然后,在 app.tsx 中的路由定义上调用 .lazy 方法,以导入带有非关键路由配置的懒加载/代码分割路由。
// src/app.tsx
const postsRoute = createRoute({
getParentRoute: () => rootRoute,
path: '/posts',
}).lazy(() => import('./posts.lazy').then((d) => d.Route))
// src/app.tsx
const postsRoute = createRoute({
getParentRoute: () => rootRoute,
path: '/posts',
}).lazy(() => import('./posts.lazy').then((d) => d.Route))
警告!!! 分割路由加载器是一个危险的游戏。
它可以成为减小 bundle 大小的强大工具,但正如 TanStack Router 如何分割代码? 部分所述,它是有代价的。
您可以使用路由的 loader 选项来代码分割您的数据加载逻辑。虽然此过程使保持传递给加载器的参数的类型安全变得困难,但您始终可以使用泛型 LoaderContext 类型来帮助您完成大部分工作
import { lazyFn } from '@tanstack/solid-router'
const route = createRoute({
path: '/my-route',
component: MyComponent,
loader: lazyFn(() => import('./loader'), 'loader'),
})
// In another file...a
export const loader = async (context: LoaderContext) => {
/// ...
}
import { lazyFn } from '@tanstack/solid-router'
const route = createRoute({
path: '/my-route',
component: MyComponent,
loader: lazyFn(() => import('./loader'), 'loader'),
})
// In another file...a
export const loader = async (context: LoaderContext) => {
/// ...
}
如果您使用基于文件的路由,则只有在使用具有自定义打包选项的 自动代码分割 时,才能分割您的 loader。
您可能已经猜到,将组件代码放在与路由不同的文件中可能会使使用路由本身变得困难。为了帮助解决这个问题,TanStack Router 导出了一个方便的 getRouteApi 函数,您可以使用它在不导入路由本身的文件中访问路由的类型安全 API。
import { createRoute } from '@tanstack/solid-router'
import { MyComponent } from './MyComponent'
const route = createRoute({
path: '/my-route',
loader: () => ({
foo: 'bar',
}),
component: MyComponent,
})
import { createRoute } from '@tanstack/solid-router'
import { MyComponent } from './MyComponent'
const route = createRoute({
path: '/my-route',
loader: () => ({
foo: 'bar',
}),
component: MyComponent,
})
import { getRouteApi } from '@tanstack/solid-router'
const route = getRouteApi('/my-route')
export function MyComponent() {
const loaderData = route.useLoaderData()
// ^? { foo: string }
return <div>...</div>
}
import { getRouteApi } from '@tanstack/solid-router'
const route = getRouteApi('/my-route')
export function MyComponent() {
const loaderData = route.useLoaderData()
// ^? { foo: string }
return <div>...</div>
}
getRouteApi 函数对于访问其他类型安全的 API 非常有用
您的每周 JavaScript 新闻。每周一免费发送给超过 100,000 名开发者。