大型前端项目管理 - Monorepo

项目级 monorepo 策略最佳实践

Monorepo

Monorepo 是管理项目代码的一个方式,指在一个项目仓库 (repo) 中管理多个模块/包 (package),不同于常见的每个模块建一个 repo。
monorepo 好处是统一的工作流和Code Sharing。搭建一套脚手架,就能管理(构建、测试、发布)多个 package,统一测试、统一发版。
坏处也很明显,就是repo的体积会比较大,由于每个 package 都有自己的package.json,会安装自己的node_modules,但是大概率会有很多包是重复的,这就使本来就很大的 node_modues 变得更大。
目前常见的monorepo解决方案是 Lernayarnworkspaces 特性
对于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> <command> 在指定工作区执行命令,如:
    注意: 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 <command> 为所有工作区运行命令(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 <package>[@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 <name> [loc] 创建一个lerna管理的包
  • lerna ls 控制台打印 packages下的包名
  • lerna link 类似npm link,创建软连接 , 将相互依赖的所有包符号链接在一起

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
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 194,524评论 5 460
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 81,869评论 2 371
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 141,813评论 0 320
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 52,210评论 1 263
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 61,085评论 4 355
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 46,117评论 1 272
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 36,533评论 3 381
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 35,219评论 0 253
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 39,487评论 1 290
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 34,582评论 2 309
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 36,362评论 1 326
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 32,218评论 3 312
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 37,589评论 3 299
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 28,899评论 0 17
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 30,176评论 1 250
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 41,503评论 2 341
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 40,707评论 2 335