「手把手教学」拆解 Monorepo 搭建与管理全流程

第一部分:Monorepo 基础认知与环境准备

1.1 什么是 Monorepo?

核心概念:将多个相关项目存放在同一个代码仓库中的代码管理策略
优势

  • 代码共享更便捷(组件/工具函数复用)
  • 依赖管理更统一(避免版本碎片化)
  • 跨项目修改更安全(原子提交)
  • CI/CD 流程更高效(统一构建部署)
1.2 工具选择

为什么用 pnpm

  • 硬链接机制节省磁盘空间
  • 依赖提升加速安装速度
  • 完美支持 workspace 特性

环境准备

# 安装 Node.js(>=16.17)
node -v
# 安装 pnpm
npm install -g pnpm
pnpm -v  # 确认版本 >=7.0.0
1.3 初始化项目结构
mkdir monorepo-demo && cd monorepo-demo
pnpm init  # 生成根 package.json

基础目录结构

├── package.json
├── pnpm-workspace.yaml  # 工作区配置文件
├── packages/    # 子包目录
│   ├── core/    # 核心工具库
│   └── web-app/ # 前端应用
└── apps/        # 应用层目录(可选)

第二部分:配置 Workspace 与创建子包

2.1 创建 workspace 配置文件

在项目根目录创建 pnpm-workspace.yaml

packages:
  - "packages/*"    # 必选:核心包目录
  - "apps/*"        # 可选:应用目录(如前端/后端)
2.2 创建第一个子包(示例:工具库)
mkdir -p packages/core
cd packages/core
pnpm init           # 生成子包 package.json

修改子包配置(示例):

// packages/core/package.json
{
  "name": "@demo/core",  // 使用命名空间防止冲突
  "version": "1.0.0",
  "main": "src/index.ts",
  "scripts": {
    "build": "tsc"
  }
}
2.3 创建第二个子包(示例:前端应用)
mkdir -p apps/web-app
cd apps/web-app
pnpm init           # 生成应用 package.json

修改应用配置(示例):

// apps/web-app/package.json
{
  "name": "@demo/web-app",
  "version": "1.0.0",
  "scripts": {
    "dev": "vite",
    "build": "vite build"
  }
}
2.4 安装公共依赖(所有子包共享)
# 在根目录操作
pnpm add typescript -D -w  # -w 表示安装在根目录(公共依赖)
2.5 安装子包专属依赖
# 为 web-app 安装专属依赖
pnpm add vite react react-dom --filter @demo/web-app

# 为 core 安装专属依赖
pnpm add lodash @types/lodash --filter @demo/core
2.6 子包之间相互引用
# 让 web-app 使用 core 包
pnpm add @demo/core --filter @demo/web-app --workspace

此时依赖关系会被自动处理

// apps/web-app/package.json
{
  "dependencies": {
    "@demo/core": "workspace:*"  # 自动生成的本地链接
  }
}
2.7 根目录脚本配置(可选)
// package.json
{
  "scripts": {
    "install:all": "pnpm install",
    "dev": "pnpm --filter @demo/web-app dev",
    "build": "pnpm -r run build"  # -r 表示递归执行所有子包中的 build 命令
  }
}

关键配置说明

  1. --filter 参数:精准控制操作目标子包
  2. workspace:*:表示使用本地工作区最新版本
  3. -w:强制安装在根目录(公共开发依赖)
  4. -r:递归执行所有子包中的命令

第三部分:工程化配置(TypeScript + ESLint)

3.1 配置根目录 TypeScript
# 根目录安装公共 TS 配置
pnpm add typescript @types/node -D -w

创建根目录 tsconfig.base.json

{
  "compilerOptions": {
    "target": "ES2020",
    "module": "ESNext",
    "moduleResolution": "Node",
    "strict": true,
    "esModuleInterop": true,
    "skipLibCheck": true,
    "forceConsistentCasingInFileNames": true,
    "baseUrl": ".",
    "paths": {
      "@demo/*": ["packages/*/src"]  // 路径别名配置
    }
  },
  "exclude": ["node_modules"]
}
3.2 子包继承 TS 配置

示例:core 包配置

// packages/core/tsconfig.json
{
  "extends": "../../tsconfig.base.json",
  "include": ["src/**/*.ts"],
  "compilerOptions": {
    "outDir": "dist",
    "rootDir": "src"
  }
}
3.3 配置统一 ESLint
# 根目录安装 ESLint 全家桶
pnpm add eslint @typescript-eslint/parser @typescript-eslint/eslint-plugin -D -w

创建 .eslintrc.cjs

module.exports = {
  root: true,
  extends: [
    'eslint:recommended',
    'plugin:@typescript-eslint/recommended'
  ],
  parser: '@typescript-eslint/parser',
  plugins: ['@typescript-eslint'],
  rules: {
    // 自定义规则
  }
}
3.4 配置 Vite 应用(示例)
# 为 web-app 安装 Vite 相关依赖
pnpm add vite @vitejs/plugin-react @types/react @types/react-dom --filter @demo/web-app

创建 apps/web-app/vite.config.ts

import { defineConfig } from 'vite'
import react from '@vitejs/plugin-react'

export default defineConfig({
  plugins: [react()],
  resolve: {
    alias: {
      '@demo/core': '../../packages/core/src'  // 手动配置路径映射
    }
  }
})

关键技巧说明

  1. 路径别名:通过 tsconfig.paths + vite.config.alias 实现跨包引用
  2. 配置继承:子包通过 extends 复用根配置,避免重复
  3. 统一规范:ESLint 在根目录集中管理代码风格

第四部分:依赖管理高级技巧

4.1 依赖类型区分
# 公共开发依赖(所有子包共享)
pnpm add jest @types/jest -D -w

# 指定子包生产依赖
pnpm add axios --filter @demo/web-app

# 指定子包开发依赖
pnpm add @testing-library/react -D --filter @demo/web-app
4.2 依赖提升控制
# 禁止提升某些依赖(如冲突的版本)
.npmrc 添加配置:
hoist=false  # 完全禁用提升
hoist-pattern[]=*react*  # 仅提升包含 react 的依赖
4.3 版本一致性强制
# 在根 package.json 添加:
"pnpm": {
  "overrides": {
    "react": "18.2.0",  // 强制统一版本
    "typescript": "^5.0.0"
  }
}

第五部分:完整开发流程演示

5.1 启动开发环境
# 并行启动多个子包
pnpm --filter "@demo/web-app" dev
5.2 执行批量构建
# 按拓扑顺序构建(需安装 @pnpm/sort-packages)
pnpm -r --stream exec pnpm build
5.3 发布私有包
# 修改 core 包版本号
cd packages/core
pnpm version patch

# 打包并发布
pnpm build
pnpm publish --access public

第六部分:构建优化与任务管理

6.1 并行任务执行
# 并行执行所有子包的 build 命令
pnpm -r --parallel run build

# 过滤特定目录的任务
pnpm --filter "./packages/**" run test
6.2 缓存策略(以 Vite 为例)
// apps/web-app/vite.config.ts
export default defineConfig({
  cacheDir: "../../.vite",  // 共享缓存目录
  build: {
    outDir: "../../dist/web-app"  // 统一输出目录
  }
})
6.3 跨包任务依赖(拓扑排序)
# 安装拓扑排序工具
pnpm add @pnpm/sort-packages -D -w

# 按依赖顺序执行构建
pnpm -r --sort exec pnpm build

第七部分:CI/CD 集成示例

7.1 GitHub Actions 基础配置
# .github/workflows/ci.yml
name: CI
on: [push]

jobs:
  build:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - uses: pnpm/action-setup@v2
        with:
          version: 8
      - run: pnpm install
      - run: pnpm build
7.2 按需触发构建
# 仅当 packages/core 发生变更时触发
paths:
  - "packages/core/**"

第八部分:部署策略

8.1 静态资源部署(前端应用)
# 示例:部署到 AWS S3
pnpm --filter @demo/web-app exec aws s3 sync ./dist s3://your-bucket
8.2 NPM 私有包部署
# 1. 在根目录配置 .npmrc
@demo:registry=https://your-private-registry

# 2. 发布命令
pnpm --filter @demo/core publish --access public

第九部分:常见问题排查

9.1 幽灵依赖(Phantom Dependencies)

现象:报错 Module not found
解决

# 检查依赖来源
pnpm why <package-name>
# 解决方案:显式声明缺失依赖
9.2 路径别名失效

现象:TS 编译成功但运行时报错
解决

  1. 确认 tsconfig.paths 配置
  2. 检查构建工具(Vite/Webpack)的别名配置
  3. 对于 Jest 需额外配置 moduleNameMapper
9.3 构建顺序问题

现象:依赖包未优先构建
解决

# 显式指定构建顺序
pnpm --filter @demo/core run build && pnpm --filter @demo/web-app run build

第十部分:维护建议

10.1 目录结构规范
monorepo-demo/
├── .github/          # CI/CD 配置
├── .husky/           # Git hooks
├── .vscode/          # 团队统一编辑器配置
├── configs/          # 共享配置(ESLint/Jest)
│   ├── eslint-base.cjs
│   └── jest-config.js
├── packages/
│   ├── core/         # 基础库
│   ├── utils/        # 工具函数
│   └── ui/           # 组件库
└── apps/
    ├── web-app/      # 前端应用
    └── admin/        # 管理后台
10.2 变更监控工具
# 安装变更集管理工具
pnpm add @changesets/cli -D -w
pnpm changeset init

下期预告:

实战案例:完整 Monorepo 项目演示

场景描述

构建包含以下模块的 Monorepo:

  • @demo/ui: Vue 组件库
  • @demo/utils: 工具函数库
  • @demo/web: 消费者端应用
  • @demo/admin: 管理后台应用
关键命令示例
# 同时启动两个前端应用
pnpm --filter "{@demo/web,@demo/admin}" dev

# 仅更新 UI 库时触发相关构建
pnpm --filter @demo/ui... run build

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值