當前端項目變得越來越大的時候,我們通常會將公共代碼拆分出來,成爲一個個獨立的npm包進行維護。但是這樣一來,各種包之間的依賴管理就十分讓人頭疼。爲了解決這種問題,我們可以將不同的npm包項目都放在同一個項目來管理。這樣的項目開發策略也稱作monorepo。Lerna就是這樣一個你更好地進行這項工作的工具。本文將詳細介紹如何使用Lerna來輔助我們的包依賴管理。
什麼是Lerna
根據官網上的定義,Lerna是一個使用git和npm來處理多包依賴管理的工具,利用它能夠自動幫助我們管理各種模塊包之間的版本依賴關係。目前,已經有很多公共庫都使用Lerna作爲它們的模塊依賴管理工具了,如:babel, create-react-app, react-router, jest等。
常用命令
bootstrap
使用bootstrap命令,會下載所有定義在package.json中的依賴包。相當於執行npm install
,並且鏈接所有依賴包。
publish
當你完成某個包的更新後,就可以使用lerna publish
命令來發布新版本的包。執行這條命令的時候,你可以指定版本號,然後lerna就會自動給你加更新版本號,加tag並提交到git倉庫上去。
add
假設你想往你的開發項目中特定包中加入依賴,你就可以使用add
命令輕鬆實現:
# 給a, b 包中加入Lodash,會同時改變a,b模塊中packages.json文件
lerna add lodash packages/a packages/b
# 給a 包中加入jquery, 使用--dev參數是使依賴加入到devDependencies中
lerna add jquery packages/a --dev
# 你也可以使用通配符, 下面這命令,會往所有re開頭的模塊包中加入依賴
lerna add jquery packages/re-*
# 指定特定的範圍,要使用--scope參數,如下:給b包安裝a模塊
lerna add a --scope=b
clean
執行clean命令,用來刪除所有模塊下node_modules中的npm包。
import
你可以使用import命令導入已有的模塊,並且會保留所有的git commit記錄。
list
列出項目中所有的模塊。
run
在每個包含該腳本的模塊中運行npm腳本。
動手實戰
說了那麼多,我們寫個例子實操一下。
初始化項目
首先,先建一個新項目,接着我們全局安裝lerna,並執行初始化操作,再上傳到git倉庫上。:
npm intall lerna -g
mkdir lerna-demo && cd $_
lerna init
git init
git add .
git commit -m "Initial Commit"
git remote add origin http://github.com/scq000/lerna-demo.git
git push -u origin master
ls
執行完上述命令後,就會在當前目錄下生成以下幾個文件:
packages/
package.json
lerna.json
其中: packages目錄用來存放我們需要拆分的各種公共代碼庫。lerna.json文件裏面記錄了lerna的相關配置信息:
{
"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: 記錄當前項目的版本號
- npmClient: 你可以指定使用npm, cnpm或yarn來執行命令
- command.publish.ignoreChanges: 忽略特定的項
- command.publish.npmClientArgs: 當執行lerna bootstrap命令時,傳給npm install的參數
- command.publish.message: 發佈模塊的時候,填寫的commit信息
- packages: 模塊包默認所在的地址
你可以根據需要自己更改相應的配置。
新建兩個模塊
爲了演示方便,我們新建兩個模塊, moduleA和moduleB, 並讓moduleA依賴moduleB:
lerna create module-a
lerna create module-b
# 將本地包鏈接起來,可以直接引用
lerna link
修改module-b 的入口文件:
export const sayHello() {
return "hello world";
};
修改module-a 的入口文件:
const moduleB = require('module-b');
const moduleA = function() {
console.log(moduleB.sayHello());
}
export default moduleA;
發佈新模塊
完成修改後,我們可以直接發佈新的模塊
lerna publish
然後,根據提示輸入版本號等,lerna就會自動幫我們給包加上tag,並上傳到對應的倉庫中去。
希望這個簡單的例子可以能讓大家熟悉簡單的操作流程。
兩種模式
lerna允許我們使用兩種模塊來管理我們的模塊:Fixed 模式和Independent模式。
Fixed/Locked mode
這個模式也是我們初始化項目的時候默認採用的模式。在這種模式下, 你可以理解爲"全量發佈"。也就是當我們一旦有某個模塊的主版本更新了,那麼所有包都會擁有一個新的版本號。而主版本號,是管理在項目根目錄的lerna.json文件中。
Independent mode
如果你不喜歡上面這種模式,你也可以使用Independent模式來管理項目中的模塊。你只需要在初始化項目的使用指定--independent參數就可以了:
lerna init --independent
在這種模式下,我們可以獨立地更新某個包的版本號,你可以理解爲"增量發佈"。
可能遇到的問題及解決方案
打包太慢
在使用過程中,最經常遇到的問題是,執行lerna bootstrap
的時候奇慢無比。這種情況通常是因爲在每個獨立包中都重複安裝了公共依賴。在這時候,我們可以將所有公共使用的包,如react,lodash之類的移到根目錄的package.json中去,並使用lerna bootstrap --hoist
命令進行安裝。使用hoist
選項後,所有公共的依賴都只會安裝在根目錄的node_modules目錄中去,而不會在每個包目錄下的node_modules中都保留各自的依賴包。
這樣一來,lerna bootstrap
的執行效率就大大增加了。
多個包版本依賴不一致
另一個可能常遇到的問題就是多個安裝包之間版本不一致,比如,A包需要依賴1.0的lodash, 而B包則需要依賴2.0的lodash。這樣就會導致在打包過程中多了許多重複代碼。而且在同一項目下保留不同版本的npm包也難以管理。對於這個問題,在大多數情況下,我們可以嘗試使用npm包管理器提供的peerDependencies選項來固定一個版本號。既可以避免很多重複的代碼,也可以解決不同版本號所帶來的衝突問題。