npm 的 monorepo 支持通过工作区(workspaces)功能实现,允许在单个仓库中管理多个相关的包。这对于大型项目和团队协作非常有用。
工作区(Workspaces)基础
什么是工作区
工作区允许你在单个 npm 项目中管理多个包,这些包可以相互依赖,并且可以共享依赖。
基本配置
在根目录的 package.json 中配置工作区:
json{ "name": "my-monorepo", "version": "1.0.0", "private": true, "workspaces": [ "packages/*" ], "scripts": { "install": "npm install -ws", "build": "npm run build -ws", "test": "npm test -ws", "clean": "npm run clean -ws" } }
目录结构
shellmy-monorepo/ ├── package.json ├── packages/ │ ├── shared/ │ │ ├── package.json │ │ └── index.js │ ├── app/ │ │ ├── package.json │ │ └── index.js │ └── utils/ │ ├── package.json │ └── index.js ├── node_modules/ │ ├── shared/ # 符号链接到 packages/shared │ ├── app/ # 符号链接到 packages/app │ └── utils/ # 符号链接到 packages/utils └── package-lock.json
工作区配置选项
1. 数组格式
json{ "workspaces": [ "packages/*", "apps/*" ] }
2. 对象格式
json{ "workspaces": { "packages": [ "packages/*" ] } }
3. 混合格式
json{ "workspaces": { "packages": [ "packages/*", "apps/*" ], "nohoist": [ "**/lodash" ] } }
工作区命令
安装依赖
bash# 在所有工作区中安装依赖 npm install -ws npm install --workspaces # 在特定工作区中安装依赖 npm install lodash --workspace=packages/app npm install lodash -w packages/app # 安装工作区依赖 npm install ../shared --workspace=packages/app
运行脚本
bash# 在所有工作区中运行脚本 npm run build -ws npm run build --workspaces # 在特定工作区中运行脚本 npm run build --workspace=packages/app npm run build -w packages/app # 在多个工作区中运行脚本 npm run build -w packages/app -w packages/utils
更新依赖
bash# 更新所有工作区的依赖 npm update -ws # 更新特定工作区的依赖 npm update --workspace=packages/app
删除依赖
bash# 从所有工作区删除依赖 npm uninstall lodash -ws # 从特定工作区删除依赖 npm uninstall lodash --workspace=packages/app
工作区间依赖
添加工作区依赖
bash# 在 packages/app 中添加对 packages/shared 的依赖 cd packages/app npm install ../shared # 或者使用工作区名称 npm install shared --workspace=packages/app
package.json 示例
packages/app/package.json:
json{ "name": "app", "version": "1.0.0", "dependencies": { "shared": "*", "lodash": "^4.17.21" } }
packages/shared/package.json:
json{ "name": "shared", "version": "1.0.0", "dependencies": { "lodash": "^4.17.21" } }
高级配置
1. nohoist 配置
防止某些包被提升到根目录:
json{ "workspaces": { "packages": [ "packages/*" ], "nohoist": [ "**/lodash", "**/react", "**/webpack" ] } }
2. 私有包
确保工作区包不会被发布:
json{ "name": "my-monorepo", "private": true, "workspaces": [ "packages/*" ] }
3. 共享依赖
在根目录定义共享依赖:
根 package.json:
json{ "name": "my-monorepo", "version": "1.0.0", "private": true, "workspaces": [ "packages/*" ], "devDependencies": { "jest": "^29.0.0", "eslint": "^8.0.0", "typescript": "^5.0.0" } }
实际示例
React + Express Monorepo
shellmy-monorepo/ ├── package.json ├── packages/ │ ├── web/ │ │ ├── package.json │ │ └── src/ │ ├── api/ │ │ ├── package.json │ │ └── src/ │ └── shared/ │ ├── package.json │ └── src/
根 package.json:
json{ "name": "my-monorepo", "version": "1.0.0", "private": true, "workspaces": [ "packages/*" ], "scripts": { "install": "npm install -ws", "build": "npm run build -ws", "dev": "npm run dev -ws", "test": "npm test -ws", "lint": "npm run lint -ws" } }
packages/web/package.json:
json{ "name": "web", "version": "1.0.0", "dependencies": { "react": "^18.0.0", "shared": "*" } }
packages/api/package.json:
json{ "name": "api", "version": "1.0.0", "dependencies": { "express": "^4.18.0", "shared": "*" } }
packages/shared/package.json:
json{ "name": "shared", "version": "1.0.0", "dependencies": { "lodash": "^4.17.21" } }
最佳实践
1. 统一版本管理
json{ "scripts": { "version": "npm version --workspaces", "publish": "npm publish --workspaces" } }
2. 共享配置
json{ "devDependencies": { "jest": "^29.0.0", "eslint": "^8.0.0", "prettier": "^3.0.0" } }
3. 统一脚本
json{ "scripts": { "build": "npm run build -ws", "test": "npm test -ws", "lint": "npm run lint -ws", "clean": "npm run clean -ws" } }
4. 使用 Lerna(可选)
Lerna 是一个专门用于管理 JavaScript monorepo 的工具:
bash# 安装 Lerna npm install -g lerna # 初始化 Lerna lerna init # 运行命令 lerna run build lerna publish
常见问题
1. 依赖冲突
bash# 检查依赖 npm ls -ws # 使用 overrides 解决冲突 npm config set overrides '{"package": "1.2.3"}'
2. 符号链接问题
bash# 检查符号链接 ls -la node_modules/ # 重新安装 rm -rf node_modules package-lock.json npm install -ws
3. TypeScript 路径问题
tsconfig.json:
json{ "compilerOptions": { "baseUrl": ".", "paths": { "shared": ["packages/shared/src"], "app": ["packages/app/src"] } } }
4. 构建顺序问题
bash# 按顺序构建 npm run build --workspace=packages/shared npm run build --workspace=packages/app npm run build --workspace=packages/api
与其他工具集成
1. TypeScript
json{ "references": [ { "path": "./packages/shared" }, { "path": "./packages/app" } ] }
2. Jest
json{ "projects": [ "<rootDir>/packages/*/jest.config.js" ] }
3. ESLint
json{ "overrides": [ { "files": ["packages/*/src/**/*.ts"], "extends": ["@mycompany/eslint-config"] } ] }
4. Webpack
javascriptmodule.exports = { resolve: { alias: { shared: path.resolve(__dirname, 'packages/shared/src') } } };
性能优化
1. 并行构建
bash# 并行运行脚本 npm run build -ws --parallel
2. 增量构建
bash# 只构建更改的包 npm run build -ws --if-present
3. 缓存
bash# 使用缓存 npm install -ws --prefer-offline
监控和调试
1. 查看工作区信息
bash# 列出所有工作区 npm workspaces info # 查看特定工作区 npm workspaces info --workspace=packages/app
2. 调试依赖
bash# 查看依赖树 npm ls -ws # 查看特定包的依赖 npm ls shared -ws
3. 查看安装日志
bash# 详细日志 npm install -ws --verbose # 调试日志 npm install -ws --loglevel=verbose
npm 工作区是管理 monorepo 的强大工具,可以显著提高大型项目的开发效率和可维护性。