框架
版本

自动代码分割

TanStack Router 中的自动代码分割功能允许您通过懒加载路由组件及其相关数据来优化应用程序的捆绑包大小。这对于大型应用程序特别有用,您可以仅加载当前路由所需的代码来最小化初始加载时间。

要启用此功能,只需在打包器插件配置中将 autoCodeSplitting 选项设置为 true。这使得路由器能够自动处理路由的代码分割,而无需任何额外的设置。

ts
// vite.config.ts
import { defineConfig } from 'vite'
import { tanstackRouter } from '@tanstack/router-plugin/vite'

export default defineConfig({
  plugins: [
    tanstackRouter({
      autoCodeSplitting: true, // Enable automatic code splitting
    }),
  ],
})
// vite.config.ts
import { defineConfig } from 'vite'
import { tanstackRouter } from '@tanstack/router-plugin/vite'

export default defineConfig({
  plugins: [
    tanstackRouter({
      autoCodeSplitting: true, // Enable automatic code splitting
    }),
  ],
})

但这仅仅是开始!TanStack Router 的自动代码分割不仅易于启用,而且还提供了强大的自定义选项,可以根据您的特定需求和使用模式来调整路由如何分割成块,从而优化应用程序的性能。

它是如何工作的?

TanStack Router 的自动代码分割通过在“开发”和“构建”期间转换您的路由文件来工作。它会重写路由定义以使用组件和加载器的懒加载包装器,这允许打包器将这些属性分组到单独的块中。

提示

块(chunk)是包含应用程序部分代码的文件,可以按需加载。这有助于通过仅加载当前路由所需的代码来减少应用程序的初始加载时间。

因此,当您的应用程序加载时,它不包含所有路由的所有代码。相反,它只包含最初需要的路由的代码。随着用户在应用程序中导航,会按需加载额外的块。

这会无缝发生,无需您手动分割代码或管理懒加载。TanStack Router 打包器插件会处理一切,确保您的路由开箱即用就能获得性能优化。

转换过程

当您启用自动代码分割时,打包器插件会通过静态代码分析您的路由文件中的代码,将其转换为优化后的输出。

此转换过程在处理每个路由文件时会产生两个关键输出

  1. 引用文件:打包器插件会获取您的原始路由文件(例如,posts.route.tsx),并修改 componentpendingComponent 等属性的值,以使用特殊的懒加载包装器,这些包装器将在稍后获取实际代码。这些包装器指向一个“虚拟”文件,打包器稍后会解析该文件。
  2. 虚拟文件:当打包器看到对这些虚拟文件之一的请求时(例如,posts.route.tsx?tsr-split=component),它会拦截该请求以生成一个新的、最小化的即时文件,该文件包含请求属性的代码(例如,仅包含 PostsComponent)。

此过程确保您的原始代码保持清晰可读,同时实际的捆绑输出针对初始捆绑大小进行了优化。

什么会被代码分割?

将哪些内容分割成单独的块对于优化应用程序的性能至关重要。TanStack Router 使用“分割分组”的概念来确定路由的不同部分应如何捆绑在一起。

分割分组是属性数组,它们告诉 TanStack Router 如何将路由的不同部分捆绑在一起。每个分组都是您希望捆绑到一个懒加载块中的属性名称列表。

可分割的属性有

  • component
  • errorComponent
  • pendingComponent
  • notFoundComponent
  • loader

默认情况下,TanStack Router 使用以下分割分组

sh
[
  ['component'],
  ['errorComponent'],
  ['notFoundComponent']
]
[
  ['component'],
  ['errorComponent'],
  ['notFoundComponent']
]

这意味着它为每个路由创建三个单独的懒加载块。结果是

  • 一个用于主组件
  • 一个用于错误组件
  • 一个用于未找到组件。

粒度控制

对于大多数应用程序来说,使用 autoCodeSplitting: true 的默认行为就足够了。然而,TanStack Router 提供了几个选项来定制您的路由如何分割成块,允许您针对特定用例或性能需求进行优化。

全局代码分割行为(defaultBehavior

您可以通过更改打包器插件配置中的 defaultBehavior 选项来更改 TanStack Router 分割路由的方式。这允许您定义路由的不同属性应如何捆绑在一起。

例如,要将所有与 UI 相关的组件捆绑到一个块中,您可以这样配置

ts
// vite.config.ts
import { defineConfig } from 'vite'
import { tanstackRouter } from '@tanstack/router-plugin/vite'

export default defineConfig({
  plugins: [
    tanstackRouter({
      autoCodeSplitting: true,
      codeSplittingOptions: {
        defaultBehavior: [
          [
            'component',
            'pendingComponent',
            'errorComponent',
            'notFoundComponent',
          ], // Bundle all UI components together
        ],
      },
    }),
  ],
})
// vite.config.ts
import { defineConfig } from 'vite'
import { tanstackRouter } from '@tanstack/router-plugin/vite'

export default defineConfig({
  plugins: [
    tanstackRouter({
      autoCodeSplitting: true,
      codeSplittingOptions: {
        defaultBehavior: [
          [
            'component',
            'pendingComponent',
            'errorComponent',
            'notFoundComponent',
          ], // Bundle all UI components together
        ],
      },
    }),
  ],
})

高级程序化控制(splitBehavior

对于复杂的规则集,您可以使用 vite 配置中的 splitBehavior 函数来程序化地定义路由应如何根据其 routeId 分割成块。此函数允许您实现自定义逻辑来将属性组合在一起,从而对代码分割行为进行精细控制。

ts
// vite.config.ts
import { defineConfig } from 'vite'
import { tanstackRouter } from '@tanstack/router-plugin/vite'

export default defineConfig({
  plugins: [
    tanstackRouter({
      autoCodeSplitting: true,
      codeSplittingOptions: {
        splitBehavior: ({ routeId }) => {
          // For all routes under /posts, bundle the loader and component together
          if (routeId.startsWith('/posts')) {
            return [['loader', 'component']]
          }
          // All other routes will use the `defaultBehavior`
        },
      },
    }),
  ],
})
// vite.config.ts
import { defineConfig } from 'vite'
import { tanstackRouter } from '@tanstack/router-plugin/vite'

export default defineConfig({
  plugins: [
    tanstackRouter({
      autoCodeSplitting: true,
      codeSplittingOptions: {
        splitBehavior: ({ routeId }) => {
          // For all routes under /posts, bundle the loader and component together
          if (routeId.startsWith('/posts')) {
            return [['loader', 'component']]
          }
          // All other routes will use the `defaultBehavior`
        },
      },
    }),
  ],
})

按路由覆盖(codeSplitGroupings

为了获得终极控制,您可以通过添加 codeSplitGroupings 属性直接在路由文件中覆盖全局配置。这对于具有独特优化需求的路由非常有用。

tsx
// src/routes/posts.route.tsx
import { createFileRoute } from '@tanstack/react-router'
import { loadPostsData } from './-heavy-posts-utils'

export const Route = createFileRoute('/posts')({
  // For this specific route, bundle the loader and component together.
  codeSplitGroupings: [['loader', 'component']],
  loader: () => loadPostsData(),
  component: PostsComponent,
})

function PostsComponent() {
  // ...
}
// src/routes/posts.route.tsx
import { createFileRoute } from '@tanstack/react-router'
import { loadPostsData } from './-heavy-posts-utils'

export const Route = createFileRoute('/posts')({
  // For this specific route, bundle the loader and component together.
  codeSplitGroupings: [['loader', 'component']],
  loader: () => loadPostsData(),
  component: PostsComponent,
})

function PostsComponent() {
  // ...
}

这将创建一个包含该特定路由的 loadercomponent 的单个块,覆盖默认行为和打包器配置中定义的任何程序化分割行为。

配置顺序很重要

本指南到目前为止已描述了三种不同的方法来配置 TanStack Router 如何将路由分割成块。

为了确保不同的配置不会相互冲突,TanStack Router 使用以下优先级顺序

  1. 按路由覆盖:路由文件中的 codeSplitGroupings 属性具有最高优先级。这允许您为单个路由定义特定的分割分组。
  2. 程序化分割行为:打包器配置中的 splitBehavior 函数允许您定义如何根据路由的 routeId 分割路由的自定义逻辑。
  3. 默认行为:打包器配置中的 defaultBehavior 选项作为任何未定义特定覆盖或自定义逻辑的路由的备用。这是适用于所有路由的基本配置,除非被覆盖。

分割数据加载器

loader 函数负责获取路由所需的数据。默认情况下,它被捆绑到您的“引用文件”中并在初始捆绑包中加载。但是,如果您想进一步优化,也可以将 loader 分割成自己的块。

注意

loader 移动到其自己的块是一个性能权衡。它会在数据获取之前引入一次额外的服务器请求,这可能导致初始页面加载变慢。这是因为 loader 必须在路由渲染其组件之前被获取和执行。因此,我们建议将 loader 保留在初始捆绑包中,除非您有特定的理由将其分割。

ts
// vite.config.ts
import { defineConfig } from 'vite'
import { tanstackRouter } from '@tanstack/router-plugin/vite'

export default defineConfig({
  plugins: [
    tanstackRouter({
      autoCodeSplitting: true,
      codeSplittingOptions: {
        defaultBehavior: [
          ['loader'], // The loader will be in its own chunk
          ['component'],
          // ... other component groupings
        ],
      },
    }),
  ],
})
// vite.config.ts
import { defineConfig } from 'vite'
import { tanstackRouter } from '@tanstack/router-plugin/vite'

export default defineConfig({
  plugins: [
    tanstackRouter({
      autoCodeSplitting: true,
      codeSplittingOptions: {
        defaultBehavior: [
          ['loader'], // The loader will be in its own chunk
          ['component'],
          // ... other component groupings
        ],
      },
    }),
  ],
})

除非您有特定用例需要,否则我们强烈不建议分割 loader。在大多数情况下,不分割 loader 并将其保留在主捆绑包中是性能的最佳选择。

我们的合作伙伴
Code Rabbit
Netlify
Neon
Clerk
Convex
Sentry
订阅 Bytes

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

Bytes

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

订阅 Bytes

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

Bytes

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