編者按:本文做者:劉觀宇,360 奇舞團高級前端工程師、技術經理,W3C CSS 工做組成員。前端
多包合做的煩惱
在開發須要多個密切協做的軟件包時候,咱們每每將獨立的功能塊進行劃分,使得各個功能獨立的模塊分別完成,以減小相互影響,完成有效的多人合做。可是,在模塊協做時,常常會遇到一些問題:vue
依賴處理繁瑣。node
依賴的模塊,尚處在開發之中,通行的npm install、yarn等沒法從安裝源中得到。react
被依賴的模塊版本升級,模塊其餘版本須要手動管理相關的版本。git
有循環依賴的風險github
對於多個模塊的大型項目的協做管理,通常地有multirepo、monorepo和submodules等多種方式:multirepo是將多個模塊分別分爲多個倉庫,早期的Babel(Babel6之前)使用的就是這種方式;submodules是藉助git的實現,在.gitmodules中寫明引用的倉庫,在主倉庫中只保留必要的索引;monorepo則是將相關的模塊用單一的倉庫統一管理。vue-cli
上述的方式各有優劣。從目前前端工程的代碼管理來看,monorepo被不少超級repo選中。Babel、vue-cli、create-react-app都採用這種模式。npm
Babel的重要貢獻者Jamie Kyle1,在爲 Babel 6 工做的過程當中發現全部東西都拆分紅漂亮的小插件包,但同時也就須要管理數十個軟件包。所以,多包存儲庫管理工具 Lerna 應運而生。爲讓項目更好用,他對項目進行了屢次重寫,試圖讓架構更完善。下圖是Jamie Kyle的靚照@_@json
Lerna也是Babel官方如今使用的多包管理工具。bootstrap
什麼是Lerna
Lerna官網2對此給出了官方的解釋:Lerna是一個管理包含多個軟件包的JavaScript項目的工具。它能夠:
解決包之間的依賴關係。
經過git倉庫檢測改動,自動同步。
根據相關的git提交的commit,生成CHANGELOG。
Lerna是一個命令行工具,能夠將其安裝在系統全局。簡單的命令說明,可使用:lerna -h查看命令幫助。
兩種模式
Lerna分爲兩種模式:fixed模式和independent模式。兩種模式的區別在於:前者強制全部的包都使用在根目錄lerna.json中指定的版本號。然後者各個軟件包,能夠本身指定版本號。
默認的,lerna使用的是fixed模式。筆者認爲,這種模式下,全部的相關軟件包,最好以幾乎一致的發佈週期發佈,如Babel這種。而且軟件內部應該被使用者更多以「黑盒」方式對待。這是fixed模式最適應的方式。
而須要暴露內部包的細節,或者迭代頻率顯著不一致的包,建議採用independent模式。
指定爲independent模式,能夠在lerna init時加入--independent,或者將lerna.json的version字段指定爲independent。
Lerna配置
lerna.json一般位於項目的根目錄下,定義了lerna運行的主要行爲。當在根目錄下運行lerna init或lerna init --independent時,會自動生成。如下是一個典型的配置:
{
"version": "1.1.3",
"npmClient": "npm",
"command": {
"publish": {
"ignoreChanges": ["ignored-file", "*.md"],
"message": "chore(release): publish"
},
"bootstrap": {
"ignore": "component-*",
"npmClientArgs": ["--no-package-lock"]
}
},
"packages": ["packages/*"]
}
上面的配置文件中:
version指定的是全部包的統一版本號;對於independent模式,這個字段請指定爲independent;
npmClient指定的是npm的客戶端。默認的,lerna將使用npm。讀者也可依所需將程序設置爲yarn,甚至cnpm等等。
command字段,能夠對publish和bootstrap命令進行參數傳遞和命令定製。如:command.publish.ignoreChanges,用來設置一些忽略的文件,以免無關文件的提交對於版本號的變動,如README.md等等。command.bootstrap.npmClientArgs指定在bootstrap命令時,傳遞的默認參數,好比咱們會經常使用--no-package-lock來禁止package-lock.json或yarn.lock等等。
packages字段指定包所在的目錄。
Lerna命令
初始一個多包的工程
lerna init
上述命令會初始化一個多包工程。初始化以後會在根目錄生成packages目錄、lerna.json,若是使用independent模式,請使用命令:lerna init --independent
建立子包
lerna create <package> [-y]
在packages所指目錄下建立package包。
添加包
lerna add <package>[@version] [--dev] [--exact] [--scope=module名]
上述命令會添加一個包package指明的軟件包。
指定--dev是添加在devDependencies中。
指定--exact,則將用精確匹配的版本添加包。
指定--scope將只在此指明的模塊中安裝這個軟件包,不然將在全部packages目錄中的包中安裝。
對於packages目錄下的子包,將經過設立systemlink來解決依賴。
對於npm鏡像中存在的包,將安裝鏡像中的包。
運行命令
運行命令分爲兩種:任意命令和npm scripts定義的命令。
對於任意命令使用,lerna exec;對於npm scripts定義的命令使用lerna run
以lerna exec爲例:
lerna exec [--concurrency number] [--stream] [--parallel] -- <command> [..args]此命令,在全部包中運行所指定的命令。
特別地,lerna exec -- rm -rf ./node_modules將刪除全部包中的依賴。lerna exec -- npm uninstall <package>將移除全部的package依賴。
lerna exec 和 lerna run 如須要每一個子模塊相繼的執行並按順序輸出,能夠指定--concurrency 1。
對於指定了--stream的命令,將把全部子進程的輸出當即回顯此舉可能形成子進程顯示順序交叉,爲了分辨輸出來源,每一個輸出,會帶上包名;指定了--parallel的命令,則會在scope指定的範圍內,並行地執行相關地命令。
lerna run與上述命令不同的狀況在於,lerna run build將在每個包中scripts字段中執行定義的build命令。
安裝全部依賴
lerna bootstrap
上述命令安裝全部的依賴、將全部的相關連接作好,同時在全部的包中運行npm run prepublish。隨後,在全部包中運行npm run prepare。此時,全部的依賴均已完備。
發佈
lerna publish 發佈全部的包。
清理
lerna clean 刪除全部的node_modules
一些優化
合併公共依賴
咱們在開發過程當中,常常發現包依賴相似。這樣,咱們發現運行lerna bootstrap以後,會重複安裝依賴包,這樣會形成空間的浪費和效率的下降。爲此,咱們能夠把一樣的依賴包在根目錄安裝一次便可。此時,可使用lerna bootstrap --hoist命令,則公用的依賴,只會在頂層目錄安裝一次。
發佈帶有scope公有包
帶有scope的包,須要發佈時候,若是是公有的包,須要在npm publish時候使用npm publish --access public。爲了可以成功publish,並使用lerna流程,請在每一個子包的package.json中加入:
"publishConfig": {
"access": "public"
}
檢測循環依賴
lerna自己內置了檢測循環依賴的功能,若是出現循環依賴。會在bootstrap時候給出提示:
此時,請依照提示去掉循環依賴,以保證軟件包的正常運行。
文內連接
https://github.com/jamiebuilds
https://lerna.js.org/
本文分享自微信公衆號 - 魚頭的Web海洋(krissarea)。
若有侵權,請聯繫 support@oschina.cn 刪除。
本文參與「OSC源創計劃」,歡迎正在閱讀的你也加入,一塊兒分享。