Nx Monorepo中Vite Dev Server多版本依赖解析异常问题
解决Nx + Vite Monorepo中Dev Server依赖版本不符的问题
项目结构
/apps/myApp /package.json /vite.config.ts(继承根级vite.config.ts) /libs /myLib1 /package.json /myLib2 /package.json package.json package-lock.json vite.config.ts
问题重现
- 根
package.json依赖somePackage@1.2.3,myLib2单独依赖somePackage@1.1.1 - npm install后,根
node_modules安装1.2.3版本,myLib2的node_modules安装1.1.1版本,符合预期 - 仅在Vite Dev Server环境下,myLib2的代码会加载根
node_modules中的1.2.3版本;Nx build打包时则能正确使用1.1.1版本 - 通过Chrome DevTools确认,myLib2的组件实际解析的是根目录下的依赖而非自身本地依赖
问题根因
Vite的optimizeDeps预构建机制会从应用入口出发,优先抓取根node_modules中的依赖做预构建缓存。即便子库有自己的本地node_modules版本,Dev Server运行时也会复用预构建后的根依赖,导致子库的特定版本被覆盖。
临时低效方案
将冲突包逐个加入optimizeDeps.exclude数组,跳过预构建:
optimizeDeps: { exclude: ['somePackage'], // ...其他原有配置 }
高效解决方案
方案1:配置resolve规则,强制子库优先使用本地依赖
修改根级vite.config.ts,添加自定义解析逻辑,让子库的代码优先加载自身node_modules中的依赖:
// 需额外引入fs模块 import fs from 'fs'; export default defineConfig(({ mode }) => { // ...原有代码 return { // ...原有配置 resolve: { alias: fs.readdirSync(path.resolve(__dirname, 'libs')).map(lib => { const libDir = path.resolve(__dirname, 'libs', lib); const pkgPath = path.join(libDir, 'package.json'); if (!fs.existsSync(pkgPath)) return null; const pkg = require(pkgPath); const deps = Object.keys(pkg.dependencies || {}); return deps.map(dep => ({ find: dep, customResolver: (source, importer) => { // 当导入方是当前子库时,返回子库本地的依赖路径 if (importer?.includes(libDir)) { return path.resolve(libDir, 'node_modules', dep); } // 其他场景使用根目录依赖 return source; } })); }).flat().filter(Boolean), }, // ...原有配置 }; });
方案2:自动排除所有子库本地依赖的预构建
通过脚本自动扫描所有子库的依赖,批量加入optimizeDeps.exclude,无需手动逐个添加:
// 需额外引入fs模块 import fs from 'fs'; export default defineConfig(({ mode }) => { // ...原有代码 // 扫描所有子库的依赖列表 const libDeps = fs.readdirSync(path.resolve(__dirname, 'libs')).flatMap(lib => { const pkgPath = path.resolve(__dirname, 'libs', lib, 'package.json'); if (fs.existsSync(pkgPath)) { const pkg = require(pkgPath); return Object.keys(pkg.dependencies || {}); } return []; }); return { // ...原有配置 optimizeDeps: { esbuildOptions: { target: 'esnext', loader: { '.js': 'jsx', '.ts': 'tsx', }, }, // 排除所有子库本地安装的依赖,避免预构建覆盖版本 exclude: [...new Set(libDeps)], }, // ...原有配置 }; });
环境信息
- vite: 4.3.4
- nx: 16.2.2
根级vite.config.ts原始配置
/// <reference types="vitest" /> import { defineConfig, loadEnv } from 'vite'; import react from '@vitejs/plugin-react'; import commonjs from 'vite-plugin-commonjs'; import viteTsConfigPaths from 'vite-tsconfig-paths'; import path from 'path'; import { nodePolyfills } from 'vite-plugin-node-polyfills'; export default defineConfig(({ mode }) => { const env = loadEnv(mode, `${process.cwd()}/apps/myApp/env`, ''); return { // vite config cacheDir: '../../node_modules/.vite/myApp', build: { commonjsOptions: { include: [/myApp/, /node_modules/], }, }, envDir: `${process.cwd()}/apps/myApp/env`, esbuild: { loader: 'tsx', include: [ 'apps/**/*.(ts|tsx|js|jsx)', 'libs/**/*.(ts|tsx|js|jsx)', ], exclude: [], }, optimizeDeps: { esbuildOptions: { target: 'esnext', loader: { '.js': 'jsx', '.ts': 'tsx', }, }, }, server: { port: 8080, host: 'localhost', fs: { // Allow serving files from two levels up to the project root allow: ['../..'], }, }, preview: { port: 8080, }, plugins: [ react(), commonjs({ }), viteTsConfigPaths({ root: '../../', }), nodePolyfills({ include: ['util'], globals: { Buffer: true, }, }), ], }; });
内容的提问来源于stack exchange,提问作者Mark McKim
相关产品推荐
相关产品推荐

