Monorepo及其相关工具

Monorepo已经成为一种常见的项目管理策略,它允许开发者在一个集中的代码库中管理和维护多个项目。
本次技术分享将介绍Monorepo的概念,通过常见的前端项目管理痛点,讲解monorepo的解决方案,以及如何使用Lerna和Nx等工具实现Monorepo管理。

项目代码仓库管理策略的演进

前端从最初的三剑客(HTML、CSS、JavaScript)到现在的各种框架、库、工具的百花齐放,前端项目的代码量也越来越多。在项目代码量不断增大的情况下,项目代码仓库的管理策略也在不断演进。目前比较流行的代码仓库管理策略有:

  1. Monolith 策略: 流行于三剑客时代,前后端代码统一放在一个代码仓库中,前端代码和后端代码共同维护,形成一个巨石单体应用
  2. Multi-repo 策略:前后端分离时代,组件库、模块库、工具库等公共依赖会单独建立一个代码仓库,前端项目也会按照业务进行拆分成多个代码仓库,通过包管理工具(npm,yarn,pnpm)来实现项目对公共库的依赖
  3. Mono-repo 策略:微前端、微模块时代,多个业务项目最终会组合形成单一应用,公共库和项目库统一管理,公共库的升级消除了滞后性,项目库之间存在的公共代码公共模块可以及时的拆离出来,具有清晰的依赖关系,同时项目的代码风格、版本管理、提交规范、CI/CD、测试、部署等也可以得到更好的统一管理

evolution

实战讲解

实现monorepo的方案有很多,这里我们介绍两种比较流行的方案:

evolution

pnpm实现Monorepo

pnpm特性里面有Workspace,我们可以通过这个特性来实现Monorepo

  1. 创建一个monorepo项目,初始化pnpm
    1
    2
    3
    mkdir monorepo
    cd monorepo
    pnpm init
  2. 创建pnpm-workspace.yaml文件
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    # ./pnpm-workspace.yaml
    packages:
    # 项目目录
    - 'packages/*'
    # 公共库目录
    - 'components/**'
    - 'api/**'
    - 'modules/**'
    # 排除测试目录中的文件
    - '!**/test/**'
  3. 创建公共库和项目目录,并通过pnpm初始化
    1
    2
    3
    4
    5
    6
    7
    8
    mkdir packages components api modules
    # 初始化公共库目录
    cd components
    pnpm init
    # 初始化项目目录
    mkdir packages/project-{a,b,c}
    cd packages/project-a
    pnpm init
  4. 修改package.json文件中的name字段,为了方便管理,可以统一加上@monorepo前缀
    1
    2
    3
    4
    // package.json
    {
    "name": "@monorepo/modules",
    }
  5. 构建依赖关系
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    # 公共库之间的依赖
    cd modules
    pnpm add @monorepo/api
    # 项目依赖
    # project-{a,b}依赖公共库
    cd packages/project-a
    pnpm add @monorepo/api @monorepo/modules @monorepo/components
    cd packages/project-b
    pnpm add @monorepo/api @monorepo/modules @monorepo/components
    # project-c依赖project-a和project-b
    cd packages/project-c
    pnpm add @monorepo/project-a @monorepo/project-b
    1
    2
    3
    4
    5
    6
    7
    8
    // 安装完后的packages/project-a/package.json
    {
    "dependencies": {
    "@monorepo/api": "workspace:^",
    "@monorepo/components": "workspace:^",
    "@monorepo/modules": "workspace:^"
    }
    }
  6. 实例测试
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    // api/user.ts
    export type User = { name: string; age: number; sex: 'male' | 'female' }

    export const fetchUserList = () => {
    return new Promise<User[]>((resolve, reject) => {
    setTimeout(() => {
    resolve(
    new Array(10).fill(null).map((_, i) => ({
    name: 'name' + i,
    age: i,
    sex: (i & 1) === 1 ? 'male' : 'female',
    }))
    );
    }, 1000);
    });
    };

    // api/index.ts
    export * from './user'

    // package/project-a setup
    import { ref } from 'vue'
    import { fetchUserList, type User } from '@monorepo/api'
    const user = ref<User[]>([])
    fetchUserList().then((list)=>{
    user.value = list
    })
  7. 项目构建部署
    1
    2
    3
    4
    5
    6
    # package/project-a
    pnpm run build
    # 构建后进入dist目录,模拟服务启动
    cd dist
    # 本地启动服务
    live-server

我们已经通过pnpmWorkspace实现了Monorepo
同时我们仍旧面临使用体验的问题,每次构建项目都需要在项目中执行构建命令(当然也可以通过脚本自动实现),这对于开发、测试、部署、CI/CD等都难以管理
针对上面的问题,我们可以使用Lerna工具,统一管理script指令

Lerna工具使用

lerna可以很好的结合pnpm,利用各自的优势更好的实现项目管理

  1. 安装Lerna
    1
    2
    3
    4
    5
    6
    pnpm add -w -D lerna
    # 初始化
    pnpm lerna init
    pnpm lerna changed --all
    # 输出项目中的packages
    pnpm lerna list
  2. 执行后会生成lerna.json文件
    1
    2
    3
    4
    5
    {
    "$schema": "node_modules/lerna/schemas/lerna-schema.json",
    "version": "0.0.0",
    "npmClient": "pnpm"
    }
  3. 统一管理script命令
    1
    2
    3
    # 这时候可以在根目录构建project-a
    pnpm lerna run --scope project-a build
    # pnpm lerna run --scope project-a dev

目前我们已经实现了script命令中心化管理。

Nx工具使用

Nx是一个由前Google员工开发的构建系统,它利用了Google内部工具所使用的许多技术。我们可以利用Nx来解决以下问题:

  1. 代码生成

  2. task的形式管理脚本

  3. 管理、缓存、分发task

  4. Nx Console —— VS Code 插件

  5. 安装Nx

    1
    pnpm add -w -D nx@latest
  6. 展示项目依赖关系图

    1
    pnpm nx graph

    graph

  7. 代码生成

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    # 安装Nx插件
    pnpm add -w -D @nx/plugin@latest
    # 创建项目插件
    pnpm nx g @nx/plugin:plugin my-plugin
    # 生成generator
    pnpm nx generate @nx/plugin:generator my-generator --project=my-plugin
    # 在需要代码生成的项目里面执行
    pnpm add @monorepo/my-plugin
    # 通过模板生成代码
    pnpm nx generate @monorepo/my-plugin:my-generator button

Monorepo及其相关工具
https://mqpeng.github.io/2023/08/18/monorepo-pnpm/
作者
Joker Spice
发布于
2023年8月18日
许可协议