大型前端项目管理 - Monorepo

Monorepo

Monorepo 是管理项目代码的一个方式,指在一个项目仓库 (repo) 中管理多个模块/包 (package),不同于常见的每个模块建一个 repo。
monorepo 好处是统一的工作流和Code Sharing。搭建一套脚手架,就能管理(构建、测试、发布)多个 package,统一测试、统一发版。
坏处也很明显,就是repo的体积会比较大,由于每个 package 都有自己的package.json,会安装自己的node_modules,但是大概率会有很多包是重复的,这就使本来就很大的 node_modues 变得更大。
目前常见的monorepo解决方案是 Lerna 和 yarnworkspaces 特性
对于node_modules包重复安装的问题,lerna提供了--hoist选项,相同的依赖,会「提升」到 repo 根目录下安装,但……太鸡肋了,lerna 直接以字符串对比 dependency 的版本号,完全相同才提升,semver 约定在这并不起作用。
yarn作为包管理器很好的解决了这个问题,只需要在根package.json中以 workspaces 字段声明 packages目录和"private": true,yarn 就会以 monorepo 的方式管理 packages。yarn 会以 semver 约定来分析 dependencies 的版本,安装依赖时更快、占用体积更小;
启用了yarn workspace的项目,使用yarn安装依赖时,yarn会为工作区的所有包创建符号链接,在根目录的node_modules可以看到
我们可以结合lerna yarn来用

谁在用 lerna
lerna 仓库
lerna参考教程
https://segmentfault.com/a/1190000019350611
https://www.jianshu.com/p/35787ebecf2e

yarn workspace 相关命令
  • yarn workspace 在指定工作区执行命令,如:
    注意: workspace_name取包名(package.json的name属性值),add或remove内部包时带上版本号;
    下面命令会将 react 、react-dmo添加到packages/awesome-package/package.jsondevDependencies
yarn workspace awesome-package add react react-dom --dev
image.png
  • yarn workspaces run 为所有工作区运行命令(lerna run 命令有同样功能),如:
    将会在每个工作区运行 test 脚本
yarn workspaces run test
  • yarn workspaces info [--json] 显示当前项目的工作区依赖关系
yarn workspaces info

vue-next项目下打印结果: 显示了工作区的相互依赖关系


image.png
lerna常用 commands
  • lerna init 初始化lerna管理项目,生成如下目录:

packages/
package.json
lerna.json

  • lerna bootstrap --hoist 为所有项目安装依赖,并链接所有依赖包,类似于npm i
    使用--hoist选项后,所有公共的依赖都只会安装在根目录的node_modules目录中去,而不会在每个包目录下的node_modules中都保留各自的依赖包。
  • lerna clean 删除所有项目的node_modules目录
  • lerna run [script] 默认为所有的项目运行npm run [script]脚本,可以指定项目;
  • lerna changed 列出下次发版lerna publish要更新的包。
  • lerna publish 版本发布,按提示选择版本号(递增,或自定义),将会执行以下步骤:
  1. 运行lerna updated来决定哪一个包需要被publish
  2. 如果有必要,将会更新lerna.json中的version
  3. 将所有更新过的的包中的package.json的version字段更新
  4. 将所有更新过的包中的依赖更新
  5. 为新版本创建一个git commit或tag
  6. 将包publish到npm上;注意要先用npm adduser登录npm源,否则会失败;
  • lerna add [@version] [--dev] [--exact] [--peer] :可以指定为某一个或所有的包安装依赖,依赖可以是外部(npm i 安装的)也可以是内部依赖(packages/下的包,会创建符号链接),example:
  1. lerna add babel , 该命令会在package-1和package-2下安装babel
  2. lerna add react --scope=package-1 ,该命令会在package-1下安装react
  3. lerna add package-2 --scope=package-1,该命令会在package-1下安装package-2
  • lerna create [loc] 创建一个lerna管理的包
  • lerna ls 控制台打印 packages下的包名
  • lerna link 类似npm link,创建软连接 ,但是实测怎么不起作用?(lerna version:v3.22.0)

lerna工作的两种模式

- Fixed/Locked mode (default)

vue,babel都是用这种,在publish的时候,会在lerna.json文件里面"version": "0.1.5",,依据这个号,进行增加,只选择一次,其他有改动的包自动更新版本号。

- Independent mode

lerna init --independent初始化项目,lerna.json文件里面"version": "independent",
每次publish时,都将得到一个提示符,提示每个已更改的包,以指定是补丁、次要更改、主要更改还是自定义更改。

启用yarn的workspaces模式

默认是npm, 而且每个子package都有自己的node_modules,通过这样设置后,只有顶层有一个node_modules

  • 修改顶层 package.json and lerna.json
# package.json 文件加入
 "private": true,
  "workspaces": [
    "packages/*"
  ],

# lerna.json 文件加入
"useWorkspaces": true,
"npmClient": "yarn"  
说了那么多,接下来实战演示一把:
1. 初始化项目
npm intall lerna -g
mkdir lernaProject && cd $_
git init
lerna init
git add .
git commit -m "Initial Commit"
git remote add origin http://github.com/renbuzhudek/lernaProject .git
git push -u origin master

上述命令执行完成后,生成如下目录:

packages/
package.json
lerna.json

2. 新建两个模块

为了演示方便,我们新建两个模块, moduleA和moduleB, 并让moduleA依赖moduleB:

lerna create module-a
lerna create module-b
# 将本地包链接起来,可以直接引用
lerna add  module-b --scope=module-a

修改module-b 的入口文件:

module.exports = moduleB;
function moduleB() {
    return "hello world";
}

修改module-a 的入口文件:

const moduleB = require('module-b');
const moduleA = function() {
    console.log(moduleB());
}
module.exports = moduleA;
moduleA()

node调用模块a:


image.png
3. 发布新模块

完成修改后,git提交完代码,我们就可以直接发布新的模块,记得要先登录npm源
然后运行下面命令,根据提示输入版本号等,lerna会自动帮我们给包加上tag,并上传到对应的仓库中去。

lerna publish
依赖包的值可以提供一个url

会下载到node_modules里面
作用:内网部署npm镜像时,可用于下载内部包

image.png

你可能感兴趣的:(大型前端项目管理 - Monorepo)