乐闻世界logo
搜索文章和话题

npm workspaces如何实现Monorepo管理?有哪些最佳实践?

2月17日 23:20

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" } }

目录结构

shell
my-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

shell
my-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

javascript
module.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 的强大工具,可以显著提高大型项目的开发效率和可维护性。

标签:NPM