介绍
大型项目的两种管理方案
当项目规模越来越大的时候,根据分而治之的原则,我们往往会把这些项目拆成众多的子项目。对于前端来说,一个子项目一般就是一个npm包。
现在我们需要使用Git来管理这个大项目,那么我们可以有下面的两种策略:
- multirepo:每个npm包分别放在一个Git仓库中(可使用子模块把这些仓库连接起来)
- monorepo:所有包放在一个Git仓库中
需要考虑什么
无论采用哪种方案,都需要考虑一些问题:
- 这些子项目如果存在相互依赖,应该如何处理(内部依赖互相link)
- 这些子项目若涉及到构建,那么构建肯定会有个先后顺序(因为子项目的依赖关系会形成一个拓扑结构)。
- 构建的时候,一般会构建所有的子包,对于没有修改的子包也会被执行构建。需要考虑一种策略,防止不必要的构建被执行。(测试等也是同理的)
- 发布新版本的时候,如何计算各个子项目的版本
multirepo
对于相互依赖这个问题,一种常规的方法是先发布A包的新版本,然后再升级依赖A的包;在调试环境下,则可以通过npm link来处理。
对于构建顺序的问题,目前得靠人工来指定顺序。
除了上面两个问题,multirepo方案还有难以复用规范化、工作流、构建等基础设施的问题。
monorepo
首先monorepo完全不用考虑基建复用的问题,因为项目都在一个仓库里,规范化、工作流和构建等设施复用起来是非常方便的。
对于相互依赖的问题,现代化的monorepo解决方案,在安装依赖的时候自动进行链接。
对于构建顺序的问题,现代化的monorepo解决方案,在执行脚本时能够根据这些项目的依赖关系进行拓扑排序,从而保证正确的构建流程。
但是,如果项目规模巨大,monorepo会导致git clone 和安装依赖的时间很长。
monorepo 解决方案
lerna
|功能特性|依赖管理|拓扑序工作流|包版本管理|发布|
|----|----|----|----|
|lerna|基于npm,支持yarn workspace|√|√|√|
Lerna是一款经典的 monorepo解决方案。官网
新版的Lerna交由Nrwl来管理,在原本的基础上增加了:
- 分布式任务执行(基于nx cloud):可以将任务分配到多台机器上进行构建
- 计算缓存:可以根据命令的输入文件来判断命令是否需要执行,避免不必要的命令执行。
常用操作
- 初始化lerna仓库:
1 | npm init lerna |
- 创建子项目(默认创建在
./packages
)
1 | lerna create <package-name> [dest] |
- 安装依赖
1 | lerna add <package-name> [--scope=范围] |
- 查看所有包
1 | lerna list |
- 导入本地已存在的包
1 | lerna import 目录 |
- 根据项目的拓扑结构执行命令
1 | lerna run 命令名 |
- 清理 node_modules
1 | lerna clean |
- 在发布前,列出需要更新的包
lerna会通过git来对比两个tag之间,各个包的修改记录。
1 | lerna changed |
- 更新包版本并发布(只会发布修改过的包)
1 | lerna publish |
在初始化的时候,版本管理模式有下面两种,不同的模式会有不同的操作:
fixed
固定模式,所有包采用统一的版本号independant
独立模式,每个包都采用不同的版本号,发布时每个包都需要独立指定版本号
注意:
- 约定好发布分支,不要使用其他分支进行发布(lerna不会检测,所以使用不同的分支发布会出现重复发布的情况)
- 如果发布失败,可以使用
lerna publish from-git
根据当前的 git tag 重新发布版本
Pnpm
|功能特性|依赖管理|拓扑序工作流|包版本管理|发布|
|----|----|----|----|
|lerna|√|√|×,需要借助changesets或者rush|√|
pnpm是一款新的包管理工具,它能够很好地利用磁盘缓存来减少空间占用,同时通过特殊的node_modules管理策略来防止幽灵依赖。
pnpm同时支持 workspace
模式,以monorepo的方式管理项目。
Yarn
|功能特性|依赖管理|拓扑序工作流|包版本管理|发布|
|----|----|----|----|
|lerna|√|√|×|√|
yarn 也支持 workspace
模式管理项目,但是会将所有依赖提升到根node_modules,加剧了幽灵依赖的问题。
参考
- 现代前端工程为什么越来越离不开 Monorepo?:https://juejin.cn/post/6944877410827370504