cssmagic/Footprint

业务项目 Monorepo 操作指南

cssmagic opened this issue · 0 comments

概述

背景

在前端领域,“Monorepo” 通常特指由 Lerna 管理的单体仓库,多个子项目放入一个大仓库中统一管理。这种代码管理模式广泛应用于大型开源项目,比如 Babel、Vue 等。

对于那些包含了多个子项目的业务项目来说,借鉴 Monorepo 模式往往可以改善日常维护效率和体验。

目的和效果

  • 所有子项目的代码相对独立,同时又存在于同一个代码仓库中。
  • 所有共公的配置文件最大程度共用。
  • 把所有子项目的依赖(node_modules 目录)提升到根级,避免重复安装依赖浪费磁盘空间。
  • 公共依赖包在开发调试时,无需发版(或手动 npm link)就可以直接在业务项目中看效果。

工具

  • yarn —— 以 “Workspace 模式” 管理所有子项目的依赖。
  • lerna —— 以根项目的视角对各个子项目进行管理。

为方便日常操作,建议把 yarn 和 lerna 安装为全局模块:

npm install -g yarn
npm install -g lerna

目录结构

/                  # 根项目
├── node_modules/
├── packages/      # 此目录下都是子项目
│   ├── _common/       # 公共业务逻辑
│   │   ├── package.json   # 包名 @myorg/_common
│   │   ├── README.md
│   │   └── (...)
│   ├── _util/         # 公共工具库
│   │   ├── package.json   # 包名 @myorg/_util
│   │   ├── README.md
│   │   └── (...)
│   ├── project-a/     # 业务项目
│   │   ├── package.json   # 包名 @myorg/project-a
│   │   ├── README.md
│   │   └── (...)
│   ├── project-b/     # 业务项目
│   │   ├── package.json   # 包名 @myorg/project-b
│   │   ├── README.md
│   │   └── (...)
│   └── (...)          # 更多业务项目
├── README.md
├── lerna.json
├── package.json
└── yarn.lock

注意事项:

  • 所有子项目的包名都需要加上 scope(即包名开头的 @myorg/ 部分),避免与 NPM 官方仓库的真实包名发生冲突。

  • packages 目录下:

    • 有些子项目是业务项目(比如上面树形图中的 project-a 目录),是一个个相对独立的 Vue 应用;
    • 有些子项目是业务项目所需的公共代码(比如 _common_util 目录),它们按照 npm 包的形式来组织代码,称作 “公共依赖包”。
  • 所有业务项目的目录名不加前缀(比如 project-a);依赖包目录加下划线前缀,以实现置顶效果(比如 _common_util 等)。

核心配置

/package.json

{
	"private": true,
	"workspaces": [
		"packages/*"
	]
}

此配置的作用是启用 yarn 的 Workspace 模式,即可实现:

  • 子项目的依赖安装到根级 node_modules 目录。
  • 所有子项目会作为 npm 包软链到根级的 node_modules 目录。

/lerna.json

{
	"npmClient": "yarn",
	"useWorkspaces": true,
	"version": "independent"
}

此配置的作用:

  • 启用 lerna 的 Workspace 模式,声明子项目的位置。
  • 使用 yarn 作为包管理工具,此时会 lerna 直接调用 yarn 来处理 Workspace 的依赖管理。
  • 各子项目的包版本相互独立,不需要同步。实际上对于业务项目来说,各个子项目也的版本号。

日常操作

注意:以下命令中 <sub-package> 指的是子项目的 package.json 所声明的包名,而不是它的目录名。

查看所有子项目

yarn workspaces info  # 注意这里是复数 `workspaces`

清除所有子项目的 node_modules 目录

lerna clean

执行此命名之后,需要立即安装一次依赖,否则各个子项目无法运行。

安装依赖

yarn install

由于我们在根级的 package.json 中启用了 Workspace 模式,此命令会把根项目和所有子项目的依赖统一安装到根级的 node_modules 目录。

这里很重要的一点是:Monorepo 内所有子项目也都会以软链接的方式安装到根级 node_modules 目录,这意味着各业务项目所引用的公共依赖包实际上就是同一仓库内的兄弟项目,公共依赖包无需发版或 npm link,业务项目就可以随之同步开发。

添加依赖

  • 为所有子项目添加依赖:

     lerna add lodash-es

    注:默认添加的是运行时依赖;如果需要安装开发依赖,请参考 Lerna 文档

  • 为指定子项目添加依赖:

     yarn workspace <sub-package> add lodash-es
  • 为根项目添加依赖:

     yarn add -W lodash-es

删除依赖

  • 为所有子项目删除依赖:

    目前没有快捷的解决方案。暂不打算全面使用 Yarn 2。

  • 为指定子项目删除依赖:

     yarn workspace <sub-package> remove lodash-es
  • 为根项目删除依赖:

     yarn remove -W lodash-es

运行脚本

  • 运行所有子项目的指定脚本:

     yarn workspaces run <script>  # 注意这里是复数 `workspaces`
     # or
     lerna run <script>

    注:如果某个子项目没有对应的脚本,Lerna 会跳过,而 Yarn 会报错。

  • 对单个子项目运行指定脚本:

     yarn workspace <sub-package> run <script>
     # or
     lerna run <script> --scope=<sub-package>
  • 对多个子项目运行指定脚本:

     lerna run <script> --scope=<glob-of-sub-packages>

    注:<glob-of-sub-packages> 是指匹配多个子项目包名的 glob 表达式。比如 @myorg/{foo,bar} 会匹配 @myorg/foo@myorg/bar

  • 运行根项目的脚本:

     yarn run <script>

参考资料


© 经验分享 · 日拱一卒   |   Star = 收藏   |   Watch = 订阅