Skip to content
On this page

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

# 工作区特定缓存
# 每个工作区可有自己的缓存策略

工作区最佳实践总结

项目设置

  1. 合理规划结构:根据功能或团队划分工作区
  2. 统一配置:在根目录设置共享配置
  3. 版本管理:考虑独立或统一版本策略
  4. 依赖共享:合理设置共享依赖

开发流程

  1. 使用工作区命令:统一管理多个包
  2. 交叉引用:使用workspace:协议
  3. 脚本统一:在根目录定义统一脚本
  4. 测试策略:考虑整体和独立测试

npm工作区提供了强大的monorepo管理能力,通过合理配置和使用,可以显著提高多包项目的开发效率。