首页
/ Vite 性能调优指南:从慢启动、慢加载到慢构建的源码级排查与优化

Vite 性能调优指南:从慢启动、慢加载到慢构建的源码级排查与优化

2026-09-04 20:30:47作者:温玫谨Lighthearted

Vite 默认是快的,但随着项目规模增长,慢启动(server start)、慢页面加载(page load)、慢构建(build)这三类性能问题会逐渐浮现。本文以官方性能指南(docs/guide/performance.md)为主线,逐条覆盖浏览器环境排查、插件审计、模块解析优化、barrel 文件规避、文件预热(warmup)以及原生工具替换六大策略,并结合当前仓库的源码实现(如 resolve.tswarmup.ts)说明每条优化背后的机制,帮助你在真实项目中定位瓶颈并给出可复制的修复方案。

一、先审查浏览器端设置

很多“Vite 变慢了”的误报其实来自浏览器环境本身。官方指南给出的两条建议:

  1. 浏览器扩展会干扰请求。某些扩展会拦截或改写网络请求,在大项目上会显著拖慢启动与刷新时间,尤其是同时开着 DevTools 时。建议为开发单独建一个不带扩展的浏览器 profile,或直接使用隐身模式(incognito)——即使是不带扩展的常规 profile,隐身模式通常也更快。
  2. 不要在工作时勾选 “Disable Cache”。Vite dev server 对预构建依赖(pre-bundled dependencies)做硬缓存,并对源码实现快速的 304 响应;一旦 DevTools 里禁用了缓存,启动和整页重载时间都会明显变差。

这一点可以从源码得到印证:HTML 中间件在发送页面时会携带 etag,模块图也维护了基于 etag 的快速路径(fast path),用于命中未变更的模块直接复用转换结果。参见 HTML 中间件模块图 etag 处理。也就是说,304/etag 机制是 Vite 保持重载快的基础设施之一,浏览器端禁用缓存等于主动放弃了这层优化。

二、审计你配置的 Vite 插件

Vite 内部插件和官方插件都按“做最少工作”的原则优化。一个典型例子:代码转换在 dev 阶段使用正则(regex)完成,而在 build 阶段则做完整解析(complete parse)以保证正确性——例如内部 importAnalysis 插件 在开发模式下以轻量正则方式重写导入,避免每个请求都付出全量 AST 解析的代价。

但社区插件的性能不在 Vite 控制范围内,它们可能是开发者体验(DX)的短板。使用额外插件时,重点检查以下三类问题:

  1. 只在小范围内使用的大依赖,应改为动态 import,以减少 Node.js 的启动耗时。官方在指南中引用了 vite-plugin-reactvite-plugin-pwa 的两个重构 PR 作为示例,都是把启动时静态加载的大依赖改为按需加载。

  2. buildStartconfigconfigResolved 钩子不应执行长耗时操作。这些钩子在 dev server 启动阶段是被 await(等待完成)的,一旦阻塞,浏览器里站点可访问的时间就被整体推迟。

  3. resolveIdloadtransform 钩子可能让部分文件比别人加载得更慢。虽然有时无法完全避免,但仍值得寻找优化空间:常见做法是在做完整转换之前,先检查 code 是否包含某个关键词、或 id 是否匹配特定扩展名,快速跳过(early return)不相关的文件。文件转换得越慢,浏览器加载站点时的请求瀑布(request waterfall)就越长。

如何测量:定位慢插件

指南提供了两个官方测量手段:

  • vite --debug plugin-transform 查看每个文件的 transform 耗时,或用社区插件 vite-plugin-inspect 做可视化检查。注意:由于异步操作的计时往往不精确,这些数字应视为粗略估计,但仍足以暴露出更昂贵的操作。
  • CPU 级 Profiling:运行 vite --profile,访问站点后在终端按 p + enter,即可记录一份 .cpuprofile。之后可用 speedscope 之类的工具打开 profile,识别瓶颈;也可以把 profile 分享给 Vite 团队协助定位性能问题。

三、减少 Resolve 操作(文件系统检查)

在最坏情况下频繁命中会让导入路径解析成为昂贵操作。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 次文件系统检查;隐式导入(省略扩展名)越多,累计开销越大。这一流程在源码中对应 resolve 插件的 tryFsResolve:当直接文件不存在时,tryCleanFsResolve 会进入“按扩展名逐个尝试”的分支(tryResolveRealFileWithExtensions,见 L1163 附近的循环),逐个用 extensions 列表拼接真实文件并做 fs 检查。

优化手段

  • 显式写出导入扩展名,如 import './Component.jsx',可让解析命中第一次检查即返回;
  • 收窄 resolve.extensions 列表,减少通用的文件系统检查次数——但务必确认收窄后的列表对 node_modules 中的包同样有效,否则会破坏依赖解析;
  • 如果你是插件作者,只在确有必要时才调用 this.resolve,减少上述检查被触发的次数。

TypeScript 提示:在 tsconfig.jsoncompilerOptions 中启用 "moduleResolution": "bundler""allowImportingTsExtensions": true,即可在代码中直接使用 .ts / .tsx 扩展名,既满足 IDE 支持,又能让导入路径显式化。

四、避免 Barrel 文件

Barrel 文件是同一个目录中把多个文件的 API 集中再导出的文件,例如:

export * from './color.js'
export * from './dom.js'
export * from './slash.js'

当你只导入其中一个 API(如 import { slash } from './utils')时,barrel 文件里引用的所有文件都必须被获取并转换——因为 slash 可能来自其中任何一个,且它们可能包含初始化时执行的副作用。这意味着首次页面加载比实际需要的多加载了一堆文件,页面自然变慢。

只要项目结构允许,应尽量避免 barrel 文件,改为直接导入具体模块:

import { slash } from './utils/slash.js'

官方指南中还引用了 issue #8237 作为该问题的详细讨论,可结合搜索查阅。

五、预热高频使用的文件(server.warmup)

Vite dev server 只在浏览器请求文件时才做转换,这使得它能快速启动、且只为用到的文件付出转换成本;它也会预判某些文件即将被请求而提前转换(pre-transform)。但请求瀑布仍可能发生:导入关系只有文件转换之后才能被知晓。

假设存在如下导入链(左边文件导入右边文件):

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

如果 BigComponent.vue 转换耗时较长,big-utils.js 就得排队等待,依此类推——即使内置了预转换,仍然存在“内部瀑布”。

Vite 允许你通过 server.warmup 选项预热已知高频使用的文件,使其转换结果提前就绪并缓存,请求到来时立即返回。该选项类型为 { clientFiles?: string[], ssrFiles?: string[] },接受相对于 root 的文件路径数组或 tinyglobby glob 模式(见 server-options 文档):

# 先找出转换耗时高、被频繁访问的文件
vite --debug transform

典型输出:

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

然后把这些文件写进配置:

export default defineConfig({
  server: {
    warmup: {
      clientFiles: [
        './src/components/BigComponent.vue',
        './src/utils/big-utils.js',
      ],
      // ssrFiles 用于 SSR 场景,如 ['./src/server/modules/*.js']
    },
  },
})

注意:只应预热确实高频使用的文件,避免在启动时把 dev server 压垮。

从源码看,预热逻辑位于 warmup.tswarmupFiles 先把配置中的路径(含 glob 模式)展开为具体文件列表,再对每个文件调用 warmupFile——.html 文件走 transformIndexHtml(从而顺带预转换其链接的 JS 模块),其余文件则通过 environment.warmupRequest(url) 走标准转换流程并写入缓存。

另外一个零成本的收益:使用 --openserver.open 启动时,Vite 会自动预热你应用(或指定 URL)的入口文件,因此让 dev server 自动打开浏览器本身也是一次性能优化。

六、使用更轻的或原生的工具链

随着代码库增长,保持 Vite 快的核心是减少对源文件(JS/TS/CSS)的加工量。指南给出了“少做工作”与“用原生工具”两类示例:

做更少的工作:

  • 尽量使用纯 CSS 而不是 Sass/Less/Stylus——CSS 嵌套(nesting)可以交给 PostCSS / Lightning CSS 处理,省掉预处理器这一层额外转换;
  • 不要把 SVG 转换成 UI 框架组件(React、Vue 等)。把它们作为字符串或 URL 导入即可,避免每个 SVG 都走一次完整的组件编译管线。

改用原生工具:

Vite 核心基于原生工具构建,但出于兼容性和功能集考虑,部分特性默认仍使用非原生实现;对大型应用,切换到原生实现是值得的:

  • 可以尝试实验性的 Lightning CSS 支持(官方在指南中链接了对应讨论),用于原生方式处理 CSS 的转换与压缩。

小结:一条可执行的排查路径

症状 首选排查点 关键手段
慢启动 浏览器扩展/缓存、插件启动钩子 隐身模式、动态 import、缩短 config/buildStart 耗时
慢加载 隐式导入、barrel 文件、内部转换瀑布 显式扩展名、直接导入模块、server.warmup--open
转换/构建慢 社区插件 transform、重型预处理器 vite --debug plugin-transformvite --profile、原生 CSS 工具链

上述策略分别对应仓库中的真实实现:解析扩展名的逐次文件系统检查见 resolve.ts,文件预热见 warmup.ts,模块缓存与 etag 快速路径见 server/index.ts。按“先测量(--debug / --profile)、再缩小(显式导入、收窄 extensions、去 barrel)、后预热(server.warmup)”的顺序操作,可以覆盖绝大多数 Vite 项目的性能退化场景。

登录后查看全文
热门项目推荐
相关项目推荐

项目优选

收起
kernelkernel
deepin linux kernel
C
33
18
ops-transformerops-transformer
本项目是CANN提供的transformer类大模型算子库,实现网络在NPU上加速计算。
C++
1.12 K
2.72 K
ops-nnops-nn
本项目是CANN提供的神经网络类计算算子库,实现网络在NPU上加速计算。
C++
904
1.82 K
docsdocs
暂无描述
Markdown
889
5.78 K
pytorchpytorch
作为 Ascend for PyTorch 社区的核心组件,TorchNPU 是昇腾专为 PyTorch 打造的深度学习适配插件,使 PyTorch 框架能够直接调用昇腾 NPU,为开发者提供昇腾 AI 处理器的超强算力。
Python
854
1.34 K
kernelkernel
openEuler内核是openEuler操作系统的核心,既是系统性能与稳定性的基石,也是连接处理器、设备与服务的桥梁。
C
527
590
jiuwenswarmjiuwenswarm
JiuwenSwarm 是一款基于openJiuwen开发的智能AI Agent,它能够将大语言模型的强大能力,通过你日常使用的各类通讯应用,直接延伸至你的指尖。
Python
3.52 K
1.01 K
ops-mathops-math
本项目是CANN提供的数学类基础计算算子库,实现网络在NPU上加速计算。
C++
1.33 K
1.45 K
AscendNPU-IRAscendNPU-IR
AscendNPU-IR是基于MLIR(Multi-Level Intermediate Representation)构建的,面向昇腾亲和算子编译时使用的中间表示,提供昇腾完备表达能力,通过编译优化提升昇腾AI处理器计算效率,支持通过生态框架使能昇腾AI处理器与深度调优
C++
540
384
flutter_flutterflutter_flutter
本仓库是 Flutter SDK 与 Flutter Engine 的 OpenHarmony 适配版本,由 CPF-Flutter 团队维护。开发者可使用熟悉的 Flutter 技术栈开发 OpenHarmony 应用,3.35.7 及以后的适配版本可基于本仓库源码构建支持 OpenHarmony 的 Flutter Engine。
Dart
1.17 K
341