You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多
文档控制台
免费开始使用

基于pnpm搭建多Node.js项目Monorepo的最佳实践与示例咨询

pnpm Monorepo 最佳实践(多应用+共享组件场景)

一、完整的 pnpm-workspace.yaml 配置

packages:
  # 纳入工作区的所有子项目
  - 'apps/**'
  - 'packages/**'
  # 排除无需参与工作区管理的目录(避免构建产物、依赖目录干扰)
  - '!apps/**/node_modules'
  - '!apps/**/dist'
  - '!packages/**/node_modules'
  - '!packages/**/dist'

# 工作区全局脚本(根目录统一执行)
scripts:
  # 按依赖顺序构建:先构建共享组件库,再构建应用(避免并行导致依赖未就绪)
  build: 'pnpm run -r --filter "./packages/**" build && pnpm run -r --filter "./apps/**" build'
  # 并行启动所有项目的开发服务
  dev: 'pnpm run -r --parallel dev'
  # 统一安装所有子项目依赖
  install: 'pnpm install'
  # 检查并更新所有子项目的依赖版本
  update: 'pnpm update -r'
  # 批量运行所有子项目的测试用例
  test: 'pnpm run -r test'

# 补充子项目缺失的Peer依赖声明
packageExtensions:
  '@your-org/ui-components':
    peerDependencies:
      react: '^18.0.0'
      react-dom: '^18.0.0'

# 锁定依赖版本策略
lockfile:
  strict: true # 强制所有子项目使用一致的依赖版本

二、标准项目结构

monorepo-root/
├── apps/
│   ├── web-app-1/          # 业务应用1
│   │   ├── package.json
│   │   ├── Dockerfile
│   │   ├── src/
│   │   └── dist/           # 构建产物(被工作区排除)
│   └── web-app-2/          # 业务应用2
│       ├── package.json
│       ├── Dockerfile
│       ├── src/
│       └── dist/
├── packages/
│   ├── ui-components/      # 共享UI组件库
│   │   ├── package.json
│   │   ├── src/
│   │   └── dist/
│   └── utils/              # 可选:共享工具函数库
│       ├── package.json
│       ├── src/
│       └── dist/
├── pnpm-workspace.yaml
├── package.json            # 根目录仅声明全局脚本与依赖版本约束
└── .gitignore

三、核心命令与实用技巧

  • 顺序构建:执行根目录的pnpm run build,先完成共享组件库编译,再构建业务应用,避免依赖缺失问题
  • 并行开发pnpm run dev同时启动所有项目的开发服务,提升效率
  • 单个项目操作:单独构建某应用:pnpm --filter web-app-1 build;给组件库新增依赖:pnpm --filter ui-components add lodash
  • 依赖版本统一:在根目录package.json中添加pnpm.overrides,强制所有子项目使用相同版本的公共依赖:
    {
      "pnpm": {
        "overrides": {
          "react": "^18.2.0",
          "react-dom": "^18.2.0"
        }
      }
    }
    

四、子项目 package.json 配置要点

共享组件库(packages/ui-components)

{
  "name": "@your-org/ui-components",
  "version": "1.0.0",
  "type": "module",
  "scripts": {
    "build": "vite build", // 按实际构建工具调整
    "dev": "vite"
  },
  "peerDependencies": {
    "react": "^18.0.0",
    "react-dom": "^18.0.0"
  },
  "main": "./dist/index.js",
  "module": "./dist/index.es.js",
  "types": "./dist/index.d.ts"
}

业务应用(apps/web-app-1)

{
  "name": "@your-org/web-app-1",
  "version": "1.0.0",
  "scripts": {
    "build": "vite build",
    "dev": "vite",
    "docker:build": "docker build -t web-app-1 ."
  },
  "dependencies": {
    "@your-org/ui-components": "workspace:*", // 引用工作区内的组件库版本
    "react": "^18.2.0",
    "react-dom": "^18.2.0"
  }
}

五、Docker构建注意事项

针对monorepo场景,应用的Dockerfile需处理依赖与构建的关联,示例如下:

# 构建阶段
FROM node:18-alpine as builder

WORKDIR /app

# 复制pnpm配置与子项目依赖声明
COPY pnpm-workspace.yaml pnpm-lock.yaml package.json ./
COPY apps/web-app-1/package.json ./apps/web-app-1/
COPY packages/ui-components/package.json ./packages/ui-components/

# 安装pnpm
RUN npm install -g pnpm

# 安装所有依赖
RUN pnpm install --frozen-lockfile

# 复制源码
COPY apps/web-app-1/src ./apps/web-app-1/src
COPY packages/ui-components/src ./packages/ui-components/src

# 执行构建
RUN pnpm run build

# 生产镜像阶段
FROM nginx:alpine

COPY --from=builder /app/apps/web-app-1/dist /usr/share/nginx/html
COPY apps/web-app-1/nginx.conf /etc/nginx/conf.d/default.conf

EXPOSE 80
CMD ["nginx", "-g", "daemon off;"]

内容的提问来源于stack exchange,提问作者Paul Verest on LinkedIn

火山引擎 最新活动