跳转到内容

性能

虽然 Vite 默认情况下速度很快,但随着项目需求的增长,性能问题可能会逐渐显现。本指南旨在帮助你识别并修复常见的性能问题,例如:

  • 服务器启动缓慢
  • 页面加载缓慢
  • 构建缓慢

检查浏览器设置

某些浏览器扩展可能会干扰请求,降低大型应用的启动和重载速度,尤其是在使用浏览器开发者工具时。在这种情况下,我们建议创建一个不带扩展程序的专用开发配置文件,或者切换到隐身模式使用 Vite 开发服务器。隐身模式通常比不带扩展的常规配置更快。

Vite 开发服务器会对预打包的依赖项进行强缓存,并为源代码实现快速的 304 响应。在浏览器开发者工具打开的情况下禁用缓存,会对启动和全页面重载时间产生很大影响。请确保在处理 Vite 服务器时未启用“禁用缓存(Disable Cache)”。

审查配置的 Vite 插件

Vite 的内置插件和官方插件经过优化,在保证与更广泛生态系统的兼容性的同时,尽可能减少不必要的工作。例如,代码转换在开发阶段使用正则表达式,但在构建阶段会进行完整的解析以确保准确性。

然而,社区插件的性能不在 Vite 的控制范围内,这可能会影响开发体验。在使用额外的 Vite 插件时,你可以关注以下几点:

  1. 仅在特定情况下使用的重型依赖项应进行动态导入,以减少 Node.js 的启动时间。重构示例:vite-plugin-react#212vite-plugin-pwa#224

  2. buildStartconfigconfigResolved 钩子不应运行冗长且耗时的操作。这些钩子在开发服务器启动期间被等待(awaited),这会延迟你在浏览器中访问站点的时间。

  3. resolveIdloadtransform 钩子可能会导致某些文件加载比其他文件慢。虽然有时不可避免,但仍值得检查是否存在可优化的地方。例如,在进行完整转换之前,检查 code 是否包含特定关键字,或 id 是否匹配特定扩展名。

    转换文件所需的时间越长,在浏览器中加载站点时请求瀑布流(request waterfall)就会越明显。

    你可以使用 vite --debug plugin-transformvite-plugin-inspect 来检查转换文件的时长。请注意,由于异步操作往往会导致计时不准确,你应该将这些数字视为粗略估计,但它仍然可以揭示代价高昂的操作。

性能分析 (Profiling)

你可以运行 vite --profile,访问站点,并在终端输入 p + enter 来记录 .cpuprofile。然后可以使用像 speedscope 这样的工具来检查性能配置文件并识别瓶颈。你还可以分享这些配置文件给 Vite 团队,以帮助我们找出性能问题。

减少解析操作

当频繁触发最坏情况时,解析导入路径可能是一项代价高昂的操作。例如,Vite 支持通过 resolve.extensions 选项来“猜测”导入路径,默认值为 ['.mjs', '.js', '.mts', '.ts', '.jsx', '.tsx', '.json']

当你尝试使用 import './Component' 导入 ./Component.jsx 时,Vite 将执行以下步骤来解析它:

  1. 检查 ./Component 是否存在,否。
  2. 检查 ./Component.mjs 是否存在,否。
  3. 检查 ./Component.js 是否存在,否。
  4. 检查 ./Component.mts 是否存在,否。
  5. 检查 ./Component.ts 是否存在,否。
  6. 检查 ./Component.jsx 是否存在,是!

如上所示,解析一个导入路径总共需要 6 次文件系统检查。隐式导入越多,解析路径所积累的时间就越多。

因此,显式声明导入路径通常更好,例如 import './Component.jsx'。你也可以缩减 resolve.extensions 的列表以减少一般的文件系统检查,但必须确保它对 node_modules 中的文件也有效。

如果你是插件作者,请确保仅在需要时才调用 this.resolve,以减少上述检查次数。

TypeScript

如果你正在使用 TypeScript,请在 tsconfig.jsoncompilerOptions 中启用 "moduleResolution": "bundler""allowImportingTsExtensions": true,以便在代码中直接使用 .ts.tsx 扩展名。

避免使用 Barrel 文件

Barrel 文件是指那些重新导出同一目录下其他文件 API 的文件。例如:

src/utils/index.js
js
export * from './color.js'
export * from './dom.js'
export * from './slash.js'

当你只导入单个 API 时,例如 import { slash } from './utils',该 barrel 文件中的所有文件都需要被获取并转换,因为它们可能包含 slash API,也可能包含在初始化时运行的副作用。这意味着在初始页面加载时,你加载的文件比实际需要的多,从而导致页面加载变慢。

如果可能,你应该避免使用 barrel 文件,并直接导入单个 API,例如 import { slash } from './utils/slash.js'。你可以阅读 issue #8237 获取更多信息。

预热频繁使用的文件

Vite 开发服务器仅在浏览器请求时才转换文件,这使得它能够快速启动,并仅对已使用的文件应用转换。它也可以在预见到某些文件即将被请求时进行预转换。然而,如果某些文件转换时间较长,仍可能会出现请求瀑布流。例如:

假设一个导入图,左侧文件导入右侧文件:

main.js -> BigComponent.vue -> big-utils.js -> large-data.json

只有在文件被转换后,才能确定导入关系。如果 BigComponent.vue 需要一些时间来转换,big-utils.js 就必须等待轮到它,依此类推。即使内置了预转换,这也会导致内部瀑布流。

Vite 允许你使用 server.warmup 选项预热你所知道的频繁使用的文件(例如 big-utils.js)。这样,当 big-utils.js 被请求时,它就已经准备好并被缓存,从而可以立即提供服务。

你可以通过运行 vite --debug transform 并检查日志来查找频繁使用的文件。

bash
vite:transform 28.72ms /@vite/client +1ms
vite:transform 62.95ms /src/components/BigComponent.vue +1ms
vite:transform 102.54ms /src/utils/big-utils.js +1ms
vite.config.js
js
export default defineConfig({
  server: {
    warmup: {
      clientFiles: [
        './src/components/BigComponent.vue',
        './src/utils/big-utils.js',
      ],
    },
  },
})

注意:你应仅预热频繁使用的文件,以免在启动时给 Vite 开发服务器造成过重负担。查看 server.warmup 选项以了解更多信息。

使用 --openserver.open 也能提升性能,因为 Vite 会自动预热应用的入口点或指定的 URL。

使用更轻量级或原生工具

保持 Vite 在代码库不断增长时依然快速的关键,在于减少对源文件(JS/TS/CSS)的处理工作量。

减少工作量的例子:

  • 尽可能使用 CSS 代替 Sass/Less/Stylus(嵌套可以由 PostCSS / Lightning CSS 处理)
  • 不要将 SVG 转换为 UI 框架组件(React, Vue 等)。直接将其作为字符串或 URL 导入。

使用原生工具的例子:

虽然 Vite 核心基于原生工具,但为了提供更好的兼容性和功能集,某些功能默认仍使用非原生工具。但对于大型应用来说,切换到原生工具可能是值得的。