www.lishuaishuai.com/engineering…html
對於維護過多個package的同窗來講,都會遇到一個選擇:這些package是放在一個倉庫裏維護仍是放在多個倉庫裏單獨維護,數量較少的時候,多個倉庫維護不會有太大問題,可是當package數量逐漸增多時,一些問題逐漸暴露出來:前端
Monorepo
的全稱是 monolithic repository,即單體式倉庫,與之對應的是 Multirepo
(multiple repository),這裏的「單」和「多」是指每一個倉庫中所管理的模塊數量。node
Multirepo
是比較傳統的作法,即每個模塊都單獨用一個倉庫來進行管理,典型案例有 webpack
,優缺點總結以下:webpack
優勢:git
缺點:github
Monorep
是把全部相關的 module 都放在一個倉庫裏進行管理,每一個 module 獨立發佈,典型案例有 babel,優缺點總結以下:web
優勢:npm
缺點:json
A tool for managing JavaScript projects with multiple packages.bootstrap
Lerna is a tool that optimizes the workflow around managing multi-package repositories with git and npm.
Lerna 是一個管理多個 npm 模塊的工具,是 Babel 本身用來維護本身的 Monorepo 並開源出的一個項目。優化維護多包的工做流,解決多個包互相依賴,且發佈須要手動維護多個包的問題。Lerna 如今已經被不少著名的項目組織使用,如:Babel, React, Vue, Angular, Ember, Meteor, Jest 。
一個基本的 Lerna 倉庫結構以下:
my-lerna-repo/
┣━ packages/
┃ ┣━ package-1/
┃ ┃ ┣━ ...
┃ ┃ ┗━ package.json
┃ ┗━ package-2/
┃ ┣━ ...
┃ ┗━ package.json
┣━ ...
┣━ lerna.json
┗━ package.json
複製代碼
全局安裝 lerna,再執行相關命令
$ npm i -g lerna $ mkdir lerna-repo && cd $_ $ lerna init 複製代碼
lerna init
命令會建立一個用來配置的 lerna.json
,文件以及用於存放全部 module
的 packages
文件夾,以下:
lerna-repo/
┣━ packages/
┣━ lerna.json
┗━ package.json
複製代碼
Lerna 提供兩種不一樣的方式來管理你的項目:Fixed
或 Independent
,默認採用 Fixed
模式,若是你想採用 Independent
模式,只需在執行 init
命令的時候加上 --independent
或 -i
參數便可。
Fixed/Locked 模式(默認) 固定模式下 Lerna 項目在單一版本線上運行。版本號保存在項目根目錄下 lerna.json
文件中的 version
下。當你運行 lerna publish
時,若是一個模塊自上次發佈版本之後有更新,則它將更新到你將要發佈的新版本。這意味着你在須要發佈新版本時只需發佈一個統一的版本便可。
Independent 模式(--independent) 獨立模式下 Lerna 容許維護人員獨立地的迭代各個包版本。每次發佈時,你都會收到每一個發生更改的包的提示,同時來指定它是 patch
,minor
,major
仍是自定義類型的迭代。
在獨立模式下,lerna.json 文件中 version 屬性的值將被忽略。
npm i
$ lerna bootstrap
複製代碼
當執行完上面的命令後,會發生如下的行爲:
npm install
安裝全部依賴npm run prepublish
npm run prepare
$ lerna add <package>[@version] [--dev] [--exact] # 命令簽名 當咱們執行此命令後,將會執行下面那2個動做: - 在每個符合要求的模塊裏安裝指明的依賴包,相似於在指定模塊文件夾中執行 `npm install <package>`。 - 更新每一個安裝了該依賴包的模塊中的 `package.json` 中的依賴包信息 # 例如 $ lerna add module-1 --scope=module-2 # 將 module-1 安裝到 module-2 $ lerna add module-1 --scope=module-2 --dev # 將 module-1 安裝到 module-2 的 devDependencies 下 $ lerna add module-1 # 將 module-1 安裝到除 module-1 之外的全部模塊 $ lerna add babel-core # 將 babel-core 安裝到全部模塊 複製代碼
$ lerna exec -- <command> [..args] # 在全部包中運行該命令 # 例如 $ lerna exec --scope=npm-list yarn remove listr # 將 npm-list 包下的 listr 卸載 $ lerna exec -- yarn remove listr # 將全部包下的 listr 卸載 複製代碼
能夠經過 clean 命令來快速刪除全部模塊中的 node_modules 文件夾。基本命令以下:
$ lerna clean
複製代碼
$ lerna updated # 或 $ lerna diff 複製代碼
Lerna 提供了兩種建立或導入模塊的方式,分別是 create
,import
。
建立一個 lerna 管理的模塊。基本命令格式以下:
$ lerna create <name> [loc]
複製代碼
name
是模塊的名稱(必填項,可包含做用域,如 @uedlinker/module-a
),必須惟一且能夠發佈(npm 倉庫中無重名已發佈包)
loc
是自定義的包路徑(選填), 會根據你在 lerna.json
文件中的 packages
的值去匹配,默認採用該數組的第一個路徑,指定其餘路徑時只要寫明路徑中的惟一值便可,例如想選擇 /user/lerna-repo/modules
這個路徑,只須要執行以下命令便可
命令執行完後,lerna 會幫咱們在指定位置建立模塊的文件夾,同時會默認在該文件夾下執行 npm init
的命令,在終端上根據根據提示填寫全部信息後會幫咱們建立對應的 package.json
文件,大體的結構以下
lerna-repo/ ┣━ packages/ ┃ ┗━ package-a/ ┃ ┣━ ... ┃ ┗━ package.json ┣━ lerna.json ┗━ package.json 複製代碼
導入一個已存在的模塊,同時保留以前的提交記錄,方便將其餘正在維護的項目合併到一塊兒。基本命令格式以下:
$ lerna import <dir>
複製代碼
dir
是本項目外的包含 npm 包的 git 倉庫路徑(相對於本項目根路徑的相對路徑)
執行後會將該模塊總體複製到指定的依賴包存放路徑下,同時會把該模塊以前全部提交記錄合併到當前項目提交記錄中
建立完畢以後,咱們能夠經過 list
命令來查看和確認如今管理的包是否符合咱們的預期,執行以下命令:
$ lerna list
複製代碼
lerna run
運行 npm script,能夠指定具體的 package。
$ lerna run <script> -- [..args] # 在全部包下運行指定 # 例如 $ lerna run test # 運行全部包的 test 命令 $ lerna run build # 運行全部包的 build 命令 $ lerna run --parallel watch # 觀看全部包並在更改時發報,流式處理前綴輸出 $ lerna run --scope my-component test # 運行 my-component 模塊下的 test 複製代碼
lerna 經過 version
命令來爲各個模塊進行版本迭代。基本命令以下:
$ lerna version [major | minor | patch | premajor | preminor | prepatch | prerelease]
複製代碼
若是不選擇這次迭代類型,則會進入交互式的提示流程來肯定這次迭代類型
例如:
$ lerna version 1.0.1 # 按照指定版本進行迭代 $ lerna version patch # 根據 semver 迭代版本號最後一位 $ lerna version # 進入交互流程選擇迭代類型 複製代碼
注意: 若是你的 lerna 項目中各個模塊版本不是按照同一個版本號維護(即建立時選擇 independent 模式),那麼會分別對各個包進行版本迭代
當執行此命令時,會發生以下行爲:
package.json
中的 version
值來反映這次更新小技巧: 你能夠在執行此命令的時候加上 ——no-push 來阻止默認的推送行爲,在你檢查確認沒有錯誤後再執行 git push 推送
--conventional-changelog
$ lerna version --conventional-commits
複製代碼
version
支持根據符合規範的提交記錄在每一個模塊中自動建立和更新 CHANGELOG.md
文件,同時還會根據提交記錄來肯定這次迭代的類型。只須要在執行命令的時候帶上 --conventional-changelog
參數便可
--changelog-preset
$ lerna version --conventional-commits --changelog-preset angular-bitbucket
複製代碼
changelog 默認的預設是 angular
,你能夠經過這個參數來選擇你想要的預設建立和更新 CHANGELOG.md
預設的名字在解析的時候會被增添 conventional-changelog-
前綴,若是你設置的是 angular
,那麼實際加載預設的時候會去找 conventional-changelog-angular
這個包,若是是帶域的包,則須要按照 @scope/name
的規則去指明,最後會被解析成 @scope/conventional-changelog-name
。
小技巧: 上述 2 個參數也能夠直接寫在 lerna.json 文件中,這樣每次執行 lerna version 命令的時候就會默認採用上面的 2 個參數
"command": { "version": { "conventionalCommits": true, "changelogPreset": "angular" } } 複製代碼
在一切準備就緒後,咱們能夠經過 publish 命令實現一鍵發佈多個模塊。基本命令以下:
$ lerna publish
複製代碼
當執行此命令時,會發生以下行爲:
lerna version
,2.x 版本遺留的行爲)注意: Lerna 不會發布在 package.json
中將 private
屬性設置爲 true
的模塊,若是要發佈帶域的包,你還須要在 'package.json' 中設置以下內容:
"publishConfig": { "access": "public" } 複製代碼
若是以前已執行過 lerna version
命令,這裏若是直接執行 lerna publish
會提示沒有發現有更新的包須要更新,咱們能夠經過從遠端的 git 倉庫來發布:
lerna publish from-git
複製代碼
{ "version": "1.1.3", "npmClient": "npm", "command": { "publish": { "ignoreChanges": [ "ignored-file", "*.md" ] }, "bootstrap": { "ignore": "component-*", "npmClientArgs": ["--no-package-lock"] } }, "packages": ["packages/*"] } 複製代碼
version:當前庫的版本 npmClient: 容許指定命令使用的client, 默認是 npm, 能夠設置成 yarn command.publish.ignoreChanges:能夠指定那些目錄或者文件的變動不會被publish command.bootstrap.ignore:指定不受 bootstrap 命令影響的包 command.bootstrap.npmClientArgs:指定默認傳給 lerna bootstrap 命令的參數 command.bootstrap.scope:指定那些包會受 lerna bootstrap 命令影響 packages:指定包所在的目錄
最後咱們來講說 Monorepo 的適用場景
另外,還須要:
基礎建設是指強大的構建工具,能知足全部模塊的 build 需求(純前端項目的話,build 壓力不大)
Monorepo 環境下,而且鼓勵改別人的代碼,一方面須要持續集成機制(例如 React – CircleCI)確認修改帶來的影響,另外一方面還須要不一樣團隊之間互相信任。
sosout.github.io/2018/07/21/… www.uedlinker.com/2018/08/17/… juejin.cn/post/684490…