vite默认自动预构建node_modules中第三方依赖,核心目的是将commonjs/umd包准确转为浏览器可加载的esm格式,借助esbuild实现快速转换并支持动态导出(如react的具名导入);需手动配置optimizedeps的include/exclude等项应对动态语法、monorepo链接包、漏识别cjs库及缓存失效等问题。

Vite 默认会自动对 node_modules 中的第三方依赖做预构建,核心目的之一就是把 CommonJS(CJS)或 UMD 格式的包安全、准确地转成浏览器可直接加载的 ES 模块(ESM)。这个过程由 esbuild 驱动,不依赖 Babel 或 Rollup,速度快且语义保留好——比如能正确处理 React 这类动态导出的 CJS 包,让 import { useState } from 'react' 这样的具名导入正常工作。
为什么需要手动配置预构建?
虽然 Vite 自动处理大部分情况,但以下场景容易出问题,需主动干预:
- 某些 CJS 包内部用了
require('./' + name)、eval、__dirname等 Node.js 特有语法,esbuild 默认无法静态分析,会跳过转换或报错; - 你引入了本地链接的 monorepo 包(如
pnpm link),Vite 默认不预构建它们,而是当作源码处理,导致 CJS 语法在浏览器中直接报错; - 像
lodash(非lodash-es)这类纯 CJS 库,若未被识别为“裸模块导入”,可能漏进预构建流程; - 开发中频繁切换分支或修改 lock 文件后,缓存未及时更新,旧的预构建产物仍被复用,引发导入行为不一致。
关键配置项:optimizeDeps
在 vite.config.js 中通过 optimizeDeps 对象控制预构建行为:
export default defineConfig({
optimizeDeps: {
// 强制将这些包纳入预构建(即使它们不在 node_modules 下,比如 linked 包)
include: ['react', 'react-dom', 'lodash', 'jsdom'],
// 明确排除某些包,避免 esbuild 处理失败(例如含二进制或特殊 require 的包)
exclude: ['sqlite3', 'canvas', 'node-fetch@2'],
// 可选:指定入口文件,缩小分析范围(适合自定义 entry 场景)
entries: ['./src/entry-client.ts'],
// 可选:启用失焦时自动重跑(适合 HMR 频繁调试场景)
disabled: false,
}
})
注意:include 不是“白名单”,而是“强制加入”;exclude 是硬性跳过,不会出现在预构建产物中,后续请求仍走原始路径(可能报错)。
常见问题应对策略
当遇到 ReferenceError: require is not defined 或 Cannot use import statement outside a module 时,按顺序检查:
- 确认该包是否在
node_modules中,且是通过裸导入(如import xxx from 'xxx')使用——只有裸导入才触发预构建; - 运行
vite --force启动,强制清空node_modules/.vite/deps并重做预构建,排除缓存污染; - 如果包本身不支持 ESM(如老旧
lodash@4),优先换用lodash-es;实在不能换,就加到include列表,并确保它没被exclude; - 对本地 linked 包,除了
include,还需在resolve.alias中显式指向其dist/esm或index.js(若已打包为 ESM)。
验证是否生效
启动开发服务器后,打开浏览器 DevTools → Network,筛选 localhost 请求,观察是否还有大量来自 node_modules/xxx/ 的细碎 JS 请求。理想状态是:CJS 包(如 react)只发出 1–2 个预构建后的 .js 请求,路径类似 /node_modules/.vite/react.js?v=xxx。同时可在终端看到类似 Pre-bundling dependencies: react, react-dom, lodash 的日志。
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南











