
我用一个面板找出构建慢的根因vite-plugin-inspect 实战诊断我做了什么我一直想知道一个问题Vite 构建慢的时候到底是哪个插件在拖后腿这个问题的难处在于——一个 Vite 项目的vite.config.ts可能挂了 20 个插件。手动排查的话逐个注释插件、重启服务、计时对比插件之间有依赖关系注释 A 要连带注释 B非常折腾。vite-plugin-inspect 就是为这个问题设计的。我搭建了一个测试仓库放了 15 个插件其中 3 个是模拟的慢插件用它完整走了一遍性能诊断流程。搭建测试场景为了贴近真实情况我构造了三种慢场景一个对所有文件做 AST 解析的插件模拟没有文件过滤的自研插件一个 Vite 生命周期内被重复调用的插件模拟 HMR 边界重算的副作用一个 resolveId 逻辑有性能问题的插件模拟 glob 扫描开销对应的vite.config.ts大概长这样importInspectfromvite-plugin-inspectexportdefaultdefineConfig({plugins:[Inspect(),// 就这一行零配置slowAstPlugin(),// 模拟无过滤的 AST 解析repeatedTransform(),// 模拟重复调用的 transformslowResolveId(),// 模拟glob 扫描// ... 其余 12 个正常插件]})仓库里有 50 个模拟模块文件.tsx/.vue/.mdx加上 node_modules 里的依赖总共会被 Vite 处理的模块约 180 个。pnpm dev冷启动稳定在12 秒左右足够复现慢场景。诊断三步走第一步Plugin Metrics 面板启动后打开http://localhost:5173/__inspect/点Plugin Metrics面板。一个柱状图把所有插件的 transform 总耗时从高到低排列出来。立即锁定了三号嫌疑插件transform 总耗时调用次数slow-ast-plugin5,420ms180vite:vue2,100ms50repeated-transform1,840ms380 ⚠️unplugin-auto-import890ms15slow-resolve-id760ms45两条线索slow-ast-plugin总耗时最高但调用 180 次——是每个模块都被处理了吗repeated-transform调用次数 380——180 个模块怎么多了一倍第二步点进个别模块看 transform 链在Modules面板里搜索一个 .tsx 模块点进去看它的完整 transform 链路。面板右侧用 CodeMirror 做了 diff 对比——左侧是原始代码右侧是每一步 transform 后的代码。这个模块的 transform 链条是这样的__load__ (虚拟步骤原始文件内容) vite:pre-alias (代码无变化 ✓) slow-ast-plugin (代码变化耗时 420ms) repeated-transform (代码无变化耗时 180ms) ⚠️ repeated-transform (代码无变化耗时 175ms) ⚠️ ← 重复调用 vite:vue (无变化) vite:esbuild (最终编译)repeated-transform对同一个模块被调用了两次但两次的输出完全一致——diff 面板全绿一行没变。这解释了为什么它的调用总数是模块数的两倍。slow-ast-plugin的问题更直接它对自己不应该处理的文件也过了完整的 AST 解析流程——node_modules里的.js文件、纯 CSS 文件都触发了解析。第三步反向验证为了确认诊断结果我给slow-ast-plugin加了文件过滤// 修复前plugin.transform(code,id){returnparseAndTransform(code)// 所有文件都处理}// 修复后plugin.transform(code,id){constVALID_EXTENSIONS/\.(tsx|vue|mdx)$/if(!VALID_EXTENSIONS.test(id)||id.includes(node_modules))returnreturnparseAndTransform(code)}给repeated-transform加了去重constseennewSetstring()plugin.transform(code,id){constkey${id}::${simpleHash(code)}if(seen.has(key))returnseen.add(key)// ... 实际逻辑}重新pnpm dev指标修复前修复后冷启动时间12.1s5.3sslow-ast-plugintransform 总耗时5,420ms1,180msrepeated-transform调用次数380180repeated-transformtransform 总耗时1,840ms350ms改动共约 20 行代码启动时间砍掉一半。不是因为优化了算法而是——插件根本不需要处理那么多模块。两个我踩到的 API 细节整个诊断过程中vite-plugin-inspect 有两个细节让我更信任它的数据1.__load__虚拟步骤每个模块的 transform 链条第一步都是一个名为__load__的虚拟步骤——它记录的是文件系统的原始内容。有了它Diff 面板才能展示原始文件 vs 第一步 transform的差异。这个步骤不参与耗时统计start end纯粹是为了 Diff 对齐。如果你自己实现类似的工具这个第零步锚点是个很讨巧的设计——不是 hack而是数据模型的自然表达。2. Plugin Metrics 的时间单位柱状图里的数字是所有模块该插件 transform 的累加耗时不是每个模块的平均耗时。这意味着如果一个插件的 transform 非常快单次 5ms但被调用了 1000 次——它在柱状图里依然可能很显眼。所以看柱状图时要同时看调用次数不要被绝对数值误导。如果手上没有 vite-plugin-inspect我试着在没有工具的情况下手动排查结论是能做但效率差 10 倍。手动流程大概是注释一半插件 → 看启动时间变没变 → 二分法缩小范围给疑似慢插件加console.time/console.timeEnd在模块源码里加performance.now()打点但插件之间有依赖时二分法会碰到注释 A 后 B 报错的问题。而且手动计时不够精确——console.time拿不到这个模块被哪些插件处理过、每次耗时是多少的全景。vite-plugin-inspect 的核心价值是一次接入全局透视不改任何被监控插件的代码数据粒度到单个模块的单次 transform。这三样合在一起手动方案做不到。这个诊断能力的通用性这套流程不只适用于找出慢插件这一个问题。同类场景还包括新接手的项目不了解每个插件做了什么看 Modules 面板能直观看出模块的转换链路升级 Vite 大版本后对比升级前后 Plugin Metrics 的耗时变化定位兼容性问题接入新插件后启动变慢不用猜面板直接告诉你哪个新插件多了多少耗时