Vite 包体分析构建快之后还要看用户下载了什么一、开发快不等于线上轻Vite 让前端开发体验变得很轻快启动快、热更新快确实舒服。但线上用户并不关心开发服务器多快他们关心页面什么时候能打开、首屏脚本多大、弱网下要等多久。构建工具快只是第一步包体分析才决定用户下载了什么。很多独立产品早期功能不多却因为引入了重型 UI 库、图表库、编辑器、日期库和 AI SDK首屏包体迅速膨胀。开发时机器快、网络好不容易察觉用户在移动网络里打开体验就会打折。包体优化不是大厂专属小产品更需要克制。二、分析链路从依赖到路由分包flowchart TD A[项目依赖] -- B[Vite 构建] B -- C[产物分析] C -- D[首屏 Chunk] C -- E[异步 Chunk] D -- F[优化依赖] E -- G[路由懒加载]包体分析要先看首屏。不是所有代码都一样重要。登录页、首页、核心工作台是用户第一时间下载的内容设置页、报表页、富文本编辑器可以按需加载。把低频功能塞进首屏包是常见浪费。还要检查重复依赖。不同库可能各自带一份日期处理、工具函数或图标包。图标尤其容易失控整包引入会让产物变大。组件库使用时要确认是否支持 tree shaking是否按需引入样式和图标。在排查重复依赖时可以用pnpm why或npm ls来看依赖树找到哪些包被重复安装了不同版本。例如我们遇到过一个项目lodash被 3 个间接依赖各自装了一份$ pnpm why lodash Legend: production dependency, optional only, dev only app1.0.0 dependencies: ant-design/icons 4.8.0 └── lodash 4.17.21 dayjs-ext 2.2.0 └── lodash 4.17.20 chart-utils 1.3.0 └── lodash 4.17.15三个不同版本的 lodash 都打包进了产物。解决办法是用 Vite 的resolve.dedupe和resolve.alias强制指向同一个版本或者推动上游依赖升级。更重要的是把lodash的整体引入改成按需引入import debounce from lodash/debounce而不是import _ from lodash。对于图标库如果只用到 20 个图标却打包了全套 2000 个建议做按需导入// 不推荐 import { UserIcon } from heroicons/react/solid; // 推荐 import UserIcon from heroicons/react/24/solid/UserIcon;后一种方式只打包用到的图标代码。图标体积从几百 KB 降到几十 KB在弱网环境下感知明显。三、配置示例引入可视化分析下面示例用rollup-plugin-visualizer生成包体报告。import { defineConfig } from vite; import { visualizer } from rollup-plugin-visualizer; export default defineConfig({ plugins: [ visualizer({ filename: dist/stats.html, gzipSize: true, brotliSize: true, }), ], });报告要结合 gzip 或 brotli 后大小看。未压缩体积能反映代码量但用户真实下载通常是压缩后。也要看执行成本某些库下载不算特别大但初始化很重会拖慢主线程。包体和运行时性能要一起评估。路由懒加载是最常见收益点。把后台报表、编辑器、管理页拆出去可以明显降低首屏压力。但拆分太碎也会增加请求数量和加载闪烁。建议按路由和重功能拆不要为了拆而拆。更细粒度的做法是对组件级拆分。比如一个 Modal 里的图表组件用户只有点开时才需要看到就不应该在首屏加载const ChartComponent lazy(() import(./ChartComponent)); function Dashboard() { const [showChart, setShowChart] useState(false); return ( div button onClick{() setShowChart(true)}查看报表/button {showChart ( Suspense fallback{div加载中.../div} ChartComponent / /Suspense )} /div ); }这样图表库可能几百 KB不会出现在首屏包体里。用户不点按钮这部分代码就永远不会下载。对于独立产品这类按需加载可以把首屏从 1.5MB 压到 600KB 以内移动网用户打开页面快 2 秒以上。还有一个容易被忽略的点CSS 的拆分。如果全局引入了一个大 CSS 文件它也会阻塞首屏渲染。建议把每个路由/页面的 CSS 拆离配合 Vite 的build.cssCodeSplit: trueexport default defineConfig({ build: { cssCodeSplit: true, rollupOptions: { output: { manualChunks(id) { if (id.includes(node_modules/echarts)) return chart-vendor; if (id.includes(node_modules/ant-design)) return antd-vendor; }, }, }, }, });manualChunks可以手动把大型依赖单独拆成 chunk方便浏览器缓存。用户第二次打开页面时这些 chunk 不变就不会重新下载。四、优化原则先删不用的再谈高级技巧包体优化第一步是删除不用的依赖。很多项目装过试验库后来没用但还留在 package.json。第二步是替换过重依赖例如只需要格式化日期不一定引入完整日期库只需要几个图标就不要打包整套图标集。可以用depcheck工具快速检查未使用的依赖npx depcheck它会列出unused dependencies和unused devDependencies一目了然。但要注意有些依赖是被配置文件或脚本动态引用的depcheck 可能误报。需要人工确认再删除。动态导入适合低频重功能例如图表、Markdown 编辑器、PDF 预览和 AI 调试面板。加载时要给明确 loading不要让用户点击后页面没反应。性能优化也要保持体验完整。最后把包体预算写进 CI。比如首屏 gzip 超过某个阈值就提醒而不是等性能变差后人工排查。独立产品人少更需要自动化提醒。否则每次就加一个小库最后会变成一座小山。预算也要按页面分层。营销首页、登录页和工作台首屏的阈值不应该一样后台报表页可以稍重但必须懒加载。每次超过预算时报告里要指出增长来自哪个依赖或哪个 chunk。只提示包体变大了意义不大能定位到源头才会促成修复。五、总结Vite 开发体验快不代表线上包体轻。通过产物分析、首屏关注、路由懒加载、依赖清理和包体预算才能知道用户实际下载了什么。构建优化最终服务的是用户打开页面的那几秒。