optimizeDeps 的 include/exclude 精细化控制实战 📅 发布时间:2026/9/4 21:29:47 👁 浏览次数: optimizeDeps 的 include/exclude 精细化控制实战在中大型前端工程体系中Vite 的optimizeDeps配置项是决定本地开发体验冷启动速度、模块解析稳定性、HMR 热更新灵敏度的“总指挥官”。在很多团队的vite.config.ts里对这个配置的处理往往非常随性要么完全留空遇到二次预构建刷新就狂按 F5 忍受要么把能在package.json里看到的所有第三方包一股脑全塞进include: [...]导致初次冷启动时间暴涨到 30 秒以上要么在 Monorepo 场景下误将内部联动开发的本地子包也丢进了预构建缓存导致修改了本地 UI 库代码后主应用死活不触发热更新。要做好 Vite 构建调优必须掌握optimizeDeps.include与optimizeDeps.exclude的精细化控制组合拳。include 与 exclude 的底层分流机制在理解具体配置前先理清 Vite Dev Server 对模块的三种处理通道[源代码中的 Import 语句] │ ├─ 1. 命中 optimizeDeps.include (或静态扫描捕获的 npm 裸模块) │ └── 走 esbuild 预构建通道 ── 输出至 .vite/deps (强缓存 Interop 包装) │ ├─ 2. 命中 optimizeDeps.exclude (或显式排除的本地 Package) │ └── 走原生浏览器 ESM 通道 ── 请求时实时通过 Vite 插件编译转换 (支持热更新) │ └─ 3. 既未 include 也未 exclude 的普通业务源码 (.vue, .ts) └── 走按需编译管道 (Vite Transform Pipeline)实战场景 1精准使用include治愈深层依赖解析场景痛点某些现代前端库采用了极其复杂的按需子路径导出或者在内部动态引用了辅助工具包。例如使用monaco-editor、echarts/core、或者floating-ui/dom时由于它们不是单文件导出浏览器在解析时会频繁触发嵌套模块的级联加载风暴。生产级精细化配置// vite.config.ts import { defineConfig } from vite; export default defineConfig({ optimizeDeps: { include: [ // 1. 明确指定的深层子路径模块 (避免运行时逐个发起 HTTP 请求) dayjs/plugin/customParseFormat, dayjs/plugin/isBetween, dayjs/plugin/timezone, dayjs/plugin/utc, // 2. 深度穿透预构建嵌套依赖 (使用 语法) // 告诉 Vite即使 pinia 在项目根目录也需要预先将其深层依赖的 vue/devtools-api 一并预构建 pinia vue/devtools-api, // 3. 复杂的非标准 ESM 库或大型编辑器模块 monaco-editor/esm/vs/editor/editor.api, monaco-editor/esm/vs/language/typescript/ts.worker, // 4. 富文本与图表辅助库 wangeditor/editor, echarts/core, echarts/charts, echarts/components, echarts/renderers, ], }, });实战场景 2Monorepo 多包联动下的exclude妙用场景痛点在pnpm workspace构建的 Monorepo 中主应用apps/web通常会依赖本地包packages/components和packages/utils。如果不将它们excludeVite 会把本地包当作静态 npm 依赖打包进.vite/deps。当你在packages/components里修改了一个按钮颜色保存后回到主应用页面毫无反应必须重启 Vite 或加--force才能看到改动严重破坏 Monorepo 的联动开发体验。生产级联动配置// apps/web/vite.config.ts import { defineConfig } from vite; import path from path; export default defineConfig({ resolve: { // 关键步骤 1: 别名直接指向本地源码目录而非子包构建产物 dist alias: { my-repo/components: path.resolve(__dirname, ../../packages/components/src), my-repo/utils: path.resolve(__dirname, ../../packages/utils/src), }, }, optimizeDeps: { // 关键步骤 2: 将本地子包显式排除出预构建 // 这样本地修改会直接走 Vite 原生的热更新通道毫秒级响应 exclude: [ my-repo/components, my-repo/utils, ], }, });实战场景 3解决 CJS 模块兼容的esbuildOptions注入某些遗留三方库虽然是 CommonJS 规范但在代码中直接引用了 Node 原生全局变量如global、process.env、Buffer在浏览器原生 ESM 环境下会直接报Uncaught ReferenceError: global is not defined。通过在optimizeDeps.esbuildOptions中注入垫片在预构建阶段一劳永逸修复// vite.config.ts import { defineConfig } from vite; import { NodeGlobalsPolyfillPlugin } from esbuild-plugins/node-globals-polyfill; export default defineConfig({ optimizeDeps: { include: [legacy-crypto-library], esbuildOptions: { // 在预构建时注入 global 与 process 垫片 define: { global: globalThis, }, plugins: [ NodeGlobalsPolyfillPlugin({ process: true, buffer: true, }), ], }, }, });调优效果与自检清单调优前问题实施精细化 include/exclude 后的效果动态弹窗首次点击卡顿 2.8 秒且页面刷新消除二次预构建点击即开延迟 80msMonorepo 子包修改后热更新失效本地子包修改实现 150ms 纯增量 HMR冷启动时间无端膨胀初次构建仅针对精准依赖冷启动耗时降低 45%生产自检三原则上线前检查在生产打包pnpm build时optimizeDeps不生效Rollup 会全量分析。必须确保本地optimizeDeps里的配置不会掩盖生产环境的 Tree-shaking 缺陷。拒绝大面积通配符严禁在include中随意写大面积无意义的顶级包名只添加确实存在深度子路径加载或二次预构建报警的特定模块。