1.项目组织管理
multirepo:将应用按照模块分别在不同的仓库中进行管理,在需要依赖时引入;倡导分而治之,存在问题:
- 开发调试及版本更新效率低下。
- 团队技术选型分散,不同库的实现风格可能存在较大差异。
- changelog梳理困难,Issues管理混乱
monorepo:将应用中所有的模块全部都放在同一个项目中;倡导集中管理,存在问题:
- 库体积超大,目录结构复杂度上升。
- 需要使用维护工具,学习成本比较高。
2.管理工具Lerna
Babel和React都是典型的monorepo,Lerna是Babel管理自身项目的开源工具。在项目创建后,Lerna会在项目下自动添加packages、lerna.json、package.json三个文件。可以分别建立多个模块的package.json,如:
packages/
module1/
package.json
module2/
package.json
module3/
package.json
然后在主目录
lerna bootstrap
命令会建立整个项目内子应用模块之间的依赖关系,不是通过硬安装,而是通过软连接指向相关依赖的。
lerna publish
命令可以将各个package一步步发布到npm中。
- Fixed/Locked模式:publish后会在lerna.json中找到指定的版本号,各个相关联的项目依赖会自动更新版本。
- independent模式:各个项目独立,每次发布时,lerna会配合git检查相关包文件变动,只发布有改动的包。
3.依赖关系
扁平依赖:不同的package依赖了不同版本的包依赖,npm3在安装依赖时,按照次序安装。但只能够部分解决依赖重复的问题,所以有npm dedupe命令。另外为了同一个项目中不同团队成员安装的版本依赖相同,会使用package-lock.json以共享指定版本的依赖。
循环依赖:模块间的相互引用。实际上并未死循环,多亏了模块加载过程中的缓存机制,但这样的机制仍会有问题,只会输出已执行部分,对于未执行部分的export来说内容为undefined。可以使用webpack插件circular-dependency-plugin来帮忙检测项目中存在的所有循环依赖。
ES模块与CommonJS规范不同,ES模块不存在缓存机制,而是动态引用依赖的模块。静态化使在编译时就能确定模块之间的依赖关系,在模块中,只生成一个引用,不会直接执行模块,在模块内真正引用依赖逻辑时,模块会从依赖中进行取值。
4.yarn workspace
使用yarn workspace管理多个子包的monorepo,开发者既可以在每个子包下使用独立的package.json管理依赖,又可以享受通过一条yarn命令安装或升级所有依赖的便利。
使用时不需要安装其他的包,只需要简单更改package.json。
{
"private": true,
"workspaces": ["workspace-1", "workspace-2"]
}
接着,可以在workspace-1和workspace-2项目中分别添加package.json文件中的内容。
{
"name": "workspace-1",
"version": "1.0.0",
"dependencies": {
"react": "16.2.3"
}
}
{
"name": "workspace-1",
"version": "1.0.0",
"dependencies": {
"react": "16.2.3",
"workspace-1": "1.0.0"
}
}
执行yarn install命令后,项目根目录下的node_modules内包含了所有声明的依赖,且各个子包的node_modules中不会重复存在依赖,只会引用根目录下中的react包。
yarn install / yarn upgrade xx // 安装或更新所有依赖
yarn workspace <workspace-name> upgrade xx // 更新某一个包内的版本
5.比较与结合
yarn workspace 和 Lerna有很多共同之处,对比一下:
- yarn workspace寄存于yarn中,不需要开发者额外安装工具即可使用,使用简单,只需要在package.json中进行相关配置,但不像Lerna那样提供了大量API。
- yarn workspace 只能在根目录中引入,不需要在各个子项目中引入。
两者搭配使用,lerna负责版本管理和发布,强大的API和设置使我们可以在开发时做到灵活细致;workspace负责依赖管理,使流程更加清晰。
开启workspace功能:
// lerna.json
{
"npmClient": "yarn",
"useWorkspaces": true,
}
// package.json
{
"private": true,
"workspaces": ["packages/*"],
}
lerna会优先使用package.json中的workspaces字段,在不存在该字段的情况下再使用lerna.json中的packages字段。
未开启workspace功能:
// lerna.json
{
"npmClient": "yarn",
"useWorkspaces": false,
"packages": ["package/11/*", "packages/12/*"]
}
// package.json
{
"private": true,
"workspaces": ["packages/21/*", "packages/22/*"],
}
yarn管理的是package.json中workspaces对应的项目路径下的依赖:21、22;而lerna管理的是lerna.json文件中packages对应的11、12