Appearance
npm 工作区
npm工作区(Workspaces)是npm 7+引入的功能,允许您在单个存储库中管理多个包。本章将详细介绍npm工作区的配置、使用和最佳实践。
工作区基础概念
什么是工作区
工作区是一种让npm在单个存储库中管理多个包的方式,也称为monorepo。它允许:
- 共享依赖
- 简化开发流程
- 统一版本管理
- 支持交叉引用
工作区优势
- 依赖共享:避免重复安装相同依赖
- 交叉引用:包之间可以轻松引用
- 统一管理:集中处理依赖更新
- 简化命令:使用单一命令操作多个包
工作区配置
基本配置
在根目录的package.json中配置工作区:
json
{
"name": "my-workspace-project",
"private": true,
"version": "1.0.0",
"workspaces": [
"packages/*",
"frontend",
"backend",
"shared"
]
}
工作区目录结构
my-workspace-project/
├── package.json
├── packages/
│ ├── package-a/
│ │ └── package.json
│ ├── package-b/
│ │ └── package.json
│ └── package-c/
│ └── package.json
├── frontend/
│ └── package.json
├── backend/
│ └── package.json
└── shared/
└── package.json
通配符配置
json
{
"name": "my-workspace-project",
"private": true,
"workspaces": [
"packages/*", // packages目录下的所有目录
"frontend/**/*", // frontend目录下的所有子目录
"components/*", // components目录下的所有目录
"packages/ui/*", // packages/ui下的所有目录
"libs/**" // libs目录及其子目录下的所有内容
]
}
工作区初始化
创建工作区项目
bash
# 创建项目目录
mkdir my-workspace-project
cd my-workspace-project
# 初始化根package.json
npm init -y
npm pkg set private=true
# 配置工作区
npm pkg set workspaces='["packages/*", "frontend", "backend"]'
# 创建工作区目录
mkdir -p packages/shared packages/utils
mkdir frontend backend
# 初始化工作区包
cd packages/shared && npm init -y && cd ../..
cd packages/utils && npm init -y && cd ../..
cd frontend && npm init -y && cd ..
cd backend && npm init -y && cd ..
使用脚手架工具
bash
# 使用Turborepo
npx create-turbo@latest
# 使用Lerna
npx lerna init
# 使用Rush
npx @microsoft/rush init
工作区命令
安装依赖
bash
# 在根目录安装依赖到所有工作区
npm install package-name --save
npm install package-name --save-dev
npm install package-name --save-optional
# 在特定工作区安装依赖
npm install package-name --workspace=frontend
npm install package-name --workspace=backend
# 在所有工作区安装依赖
npm install package-name --workspaces
运行脚本
bash
# 在特定工作区运行脚本
npm run build --workspace=frontend
npm run test --workspace=backend
# 在所有工作区运行脚本
npm run build --workspaces
npm run test --workspaces
# 在所有工作区运行脚本(如果存在)
npm run build --workspaces --if-present
# 运行并显示工作区名称
npm run test --workspaces --verbose
列出工作区
bash
# 列出所有工作区
npm workspaces list
npm workspaces list --long # 显示详细信息
# 查看工作区树
npm workspaces list --depth=1
工作区依赖管理
交叉依赖
json
// packages/shared/package.json
{
"name": "@my-workspace/shared",
"version": "1.0.0",
"main": "index.js"
}
// packages/utils/package.json
{
"name": "@my-workspace/utils",
"version": "1.0.0",
"dependencies": {
"@my-workspace/shared": "^1.0.0"
}
}
工作区协议
使用workspace:协议引用工作区包:
json
{
"name": "my-workspace-project",
"private": true,
"workspaces": [
"packages/*"
],
"dependencies": {
"@my-workspace/shared": "workspace:*",
"@my-workspace/utils": "workspace:^1.0.0"
}
}
工作区协议选项
json
{
"dependencies": {
// 引用最新版本
"shared": "workspace:*",
// 引用特定版本
"shared": "workspace:^1.0.0",
// 引用特定工作区
"shared": "workspace:packages/shared"
}
}
高级工作区配置
工作区配置文件
在根目录创建.npmrc文件:
# 禁用工作区功能
workspaces=false
# 仅在特定命令中使用工作区
recursive=false
# 设置工作区根目录
workspace-root=/path/to/root
package.json配置
json
{
"name": "my-workspace-project",
"private": true,
"version": "1.0.0",
"workspaces": {
"packages": [
"packages/*",
"frontend",
"backend"
],
"nohoist": [
"**/react",
"**/react-dom"
]
},
"scripts": {
"build": "npm run build --workspaces",
"test": "npm run test --workspaces",
"dev": "concurrently \"npm run dev --workspace=frontend\" \"npm run dev --workspace=backend\""
}
}
工作区最佳实践
项目结构
bash
# 推荐的monorepo结构
my-workspace/
├── package.json
├── .npmrc
├── README.md
├── packages/
│ ├── ui/
│ │ ├── package.json
│ │ └── src/
│ ├── api/
│ │ ├── package.json
│ │ └── src/
│ └── shared/
│ ├── package.json
│ └── src/
├── apps/
│ ├── web/
│ │ ├── package.json
│ │ └── src/
│ └── admin/
│ ├── package.json
│ └── src/
└── scripts/
└── release.js
依赖管理策略
json
// 根package.json - 共享开发依赖
{
"name": "my-workspace-root",
"private": true,
"devDependencies": {
"eslint": "^8.0.0",
"jest": "^28.0.0",
"prettier": "^2.0.0"
},
"scripts": {
"lint": "npm run lint --workspaces --if-present",
"test": "npm run test --workspaces --if-present",
"format": "prettier --write ."
}
}
json
// packages/ui/package.json - 特定依赖
{
"name": "@my-workspace/ui",
"dependencies": {
"react": "^18.0.0",
"@my-workspace/shared": "workspace:*"
},
"devDependencies": {
"@testing-library/react": "^13.0.0"
},
"scripts": {
"build": "vite build",
"dev": "vite",
"test": "jest"
}
}
工作区脚本
统一脚本管理
json
{
"name": "my-workspace-project",
"private": true,
"scripts": {
"build": "npm run build --workspaces",
"build:frontend": "npm run build --workspace=frontend",
"build:backend": "npm run build --workspace=backend",
"dev": "concurrently \"npm run dev --workspace=frontend\" \"npm run dev --workspace=backend\"",
"dev:frontend": "npm run dev --workspace=frontend",
"dev:backend": "npm run dev --workspace=backend",
"test": "npm run test --workspaces --if-present",
"test:unit": "npm run test:unit --workspaces --if-present",
"test:e2e": "npm run test:e2e --workspaces --if-present",
"lint": "npm run lint --workspaces --if-present",
"format": "prettier --write . && npm run format --workspaces --if-present",
"clean": "npm run clean --workspaces --if-present && rimraf node_modules",
"publish": "npm publish --workspaces"
}
}
并行执行
json
{
"scripts": {
"dev": "concurrently --kill-others-on-fail \"npm run dev --workspace=frontend\" \"npm run dev --workspace=backend\"",
"test:parallel": "concurrently \"npm run test --workspace=package-a\" \"npm run test --workspace=package-b\""
}
}
工作区与CI/CD
CI配置示例
# .github/workflows/ci.yml
name: CI
on: [push, pull_request]
jobs:
test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- uses: actions/setup-node@v3
with:
node-version: '18'
- name: Install dependencies
run: npm ci
- name: Run tests
run: npm test --workspaces --if-present
- name: Run lint
run: npm run lint --workspaces --if-present
构建优化
bash
# 仅构建变更的工作区
npx nx affected --target=build
# 或使用Turborepo
npx turbo build --filter=changed
工作区工具集成
与Lerna集成
json
{
"name": "my-workspace-project",
"private": true,
"workspaces": [
"packages/*"
],
"devDependencies": {
"lerna": "^6.0.0"
}
}
bash
# Lerna命令与npm工作区兼容
lerna run build
lerna exec -- npm test
与Turborepo集成
json
// turbo.json
{
"pipeline": {
"build": {
"dependsOn": ["^build"],
"outputs": ["dist/**"]
},
"test": {
"dependsOn": ["build"],
"outputs": []
},
"lint": {
"outputs": []
}
}
}
工作区故障排除
常见问题
bash
# 问题:工作区命令不生效
# 解决:确保package.json中有"private": true
npm pkg set private=true
# 问题:依赖安装失败
# 解决:检查工作区路径配置
npm workspaces list
# 问题:交叉引用不工作
# 解决:使用workspace:协议
npm install package-name@workspace:*
调试工作区
bash
# 查看工作区配置
npm config list | grep workspace
# 验证工作区
npm workspaces list --long
# 查看依赖树
npm ls --workspaces
工作区性能优化
安装优化
bash
# 使用npm ci进行快速安装
npm ci
# 并行安装依赖
npm install --prefer-offline
# 使用更快的包管理器
# pnpm或yarn在工作区场景下通常更快
缓存策略
bash
# 共享缓存
npm config set cache-max 1073741824
# 工作区特定缓存
# 每个工作区可有自己的缓存策略
工作区最佳实践总结
项目设置
- 合理规划结构:根据功能或团队划分工作区
- 统一配置:在根目录设置共享配置
- 版本管理:考虑独立或统一版本策略
- 依赖共享:合理设置共享依赖
开发流程
- 使用工作区命令:统一管理多个包
- 交叉引用:使用
workspace:协议 - 脚本统一:在根目录定义统一脚本
- 测试策略:考虑整体和独立测试
npm工作区提供了强大的monorepo管理能力,通过合理配置和使用,可以显著提高多包项目的开发效率。