鯨品堂|Lerna最佳實踐(內含大量代碼)

2022-10-17 407

在介紹本文的主角 lerna 之前,首先了解下什么是 multirepo ?什么是 monorepo ?


multirepo 指的是將模塊分為多個倉庫,monorepo 指的是將多個模塊放在一個倉庫中。


multirepo 可以讓每個團隊都擁有自己的倉庫,他們可以使用自己的構建流程、代碼規范等,但是同時也會存在很多問題,比如模塊之間如果存在相互依賴,就必須到目標倉庫里進行bug修復、構建、發版本等,相互依賴關系越復雜,處理起來就越困難。


monorepo 可以讓多個模塊共享同一個倉庫,因此他們可以共享同一套構建流程、代碼規范也可以做到統一,特別是如果存在模塊間的相互依賴的情況,查看代碼、修改bug、調試等會更加方便,因此也越來越受到大家的關注,像 Babel、React、Vue 等主流的開源倉庫都采用的 monorepo。



lerna









lerna 是一個管理工具,用于管理包含多個軟件包(package)的 JavaScript 項目,最早是 Babel 自己用來維護自己的 monorepo 并開源出的一個項目,針對使用 git 和 npm 管理多軟件包代碼倉庫的工作流程進行優化,解決多個包互相依賴,且發布需要手動維護多個包的問題。


總結一下,使用 lerna 可以幫我們解決如下幾個痛點:


多個倉庫之間可以管理管理公共的依賴包,或者單獨管理各自的依賴包


方便模塊之間的相互引用,模塊之間的調試不必發版本,lerna內部會自動進行link


lerna提供了兩種模式,支持選擇單獨針對某個包發版本或者統一發版本


多個倉庫之間可以共享統一的代碼規范,版本管理更加規范


以下會分兩個部分介紹,首先是介紹 lerna 的常規用法,然后介紹下 lerna 的最佳實踐。



基本用法









安裝

$ npm install --global lerna


創建一個git倉庫

$ git init lerna-repo && cd lerna-repo


初始化一個 lerna 倉庫

$ lerna init


注意,lerna init 可以通過參數 --independent 進入 Independent 模式,該模式可以單獨發版本。


初始化之后的工程目錄結構如下:

lerna-repo├── lerna.json├── package.json└── packageslerna.json:{"packages": ["packages/*"],"version": "0.0.0"}package.json:{"name": "root","private": true,"devDependencies": {"lerna": "^4.0.0"}}


新增package包

使用 lerna create 創建兩個包 pkg1 和 pkg2

$ lerna create pkg1$ lerna create pkg2


創建完成后的目錄結構如下:

lerna-demo├── README.md├── lerna.json├── package.json└── packages├── pkg1│ ├── README.md│ ├── __tests__│ ├── lib│ └── package.json└── pkg2├── README.md├── __tests__├── lib└── package.json


給兩個package增加公共依賴

給 pkg1 和 pkg2 這兩個包都安裝 fs-extra 這個包,pkg1 和 pkg2 的 package.json 的 dependency 會同時包含 fs-extra 這個包。

$ lerna add fs-extra


安裝 fs-extra 之后的目錄結構:

lerna-demo├── README.md├── lerna.json├── package.json└── packages├── pkg1│ ├── README.md│ ├── __tests__│ ├── lib│ ├── node_modules│ ├── package-lock.json│ └── package.json└── pkg2├── README.md├── __tests__├── lib├── node_modules├── package-lock.json└── package.json


給某個包單獨安裝指定依賴

比如給 pkg1 安裝一個 glob 包,給 pkg2 安裝一個 ora 包:

$ lerna add glob --scope pkg1$ lerna add ora --scope pkg2


其中 --scope 參數用來指定具體給哪個 package 安裝包,注意 --scope 后面的參數是對應模塊的 package.json 中的 name 字段名。


添加內部模塊之間的依賴

將 pkg1 作為 pkg2 的依賴進行安裝:

$ lerna add pkg1 --scope pkg2


需要注意的是,通過這種方式安裝的依賴,并不會將 pkg1 安裝到 pkg2 的 node_modules 里,而是通過 symlink 的形式進行關聯。


發布

以上包確認沒有問題之后,就可以通過執行 lerna publish 進行發布了。


在進行 publish 之前需要首先提交你的代碼,否則 lerna 會報錯:

lerna ERR! ENOCOMMIT No commits in this repository. Please commit something before using version.


提交代碼并關聯到 git 倉庫:

$ git add .$ git commit -m 'init'$ git remote add origin git@github.com:astonishqft/lerna-demo.git // 關聯到遠程git倉庫$ git push -u origin main


刪除某個包

將 pkg1 里面的 glob 包刪除:

$ lerna exec --scope=pkg1 npm uninstall glob


抽離公共的包

上面可以看到,pkg1 和 pkg2 都依賴了 fs-extra 這個包,而各自 package 下面的 node_modules 都進行了一次安裝,因此我們可以通過 --hoist 來抽取重復的依賴到最外層的 node_modules 目錄下,同時最外層的 package.josn 的依賴信息也不會進行更新。

$ lerna bootstrap --hoist

但是這種方式會有一個問題,不同版本號只會保留使用最多的版本,這種配置不太好,當項目中有些功能需要依賴老版本時,就會出現問題,因此這種方式不推薦使用。



最佳實踐









前面我們已經介紹了 lerna 的相關概念和基本用法,目前最常見的解決方案是基于 lerna 和 yarn workspace 的 monorepo 工作流。


由于 yarn 和 lerna 在功能上有較多的重疊,我們采用 yarn 官方推薦的做法:用 yarn 來處理依賴問題,用 lerna 來處理發布問題。


yarn workspaces 與 lerna

yarn workspaces 是 yarn 提供的 monorepo 的依賴管理機制,用于在代碼倉庫的根目錄下管理多個 package 依賴,與 lerna 不同的是,yarn workspaces 可以解決前面說的當不同的 package 依賴不同的版本號問題,yarn workspaces 會檢查每個子項目里面依賴及其版本,如果版本不一致都會安裝到各自 package 的 node_modules 中,只有依賴版本號一致的時候才會提升到頂層,而 lerna 會進到每個 package 中執行 yarn/npm install,因此會在每個 package 下生成一個 node_modules。


yarn workspaces 首先在工程的根目錄下的 package.json 中增加 "private": true 和 "workspaces”: [ "packages/*"] 配置項。"private": true 可以確保根目錄不會被發布出去,"workspaces”: [ "packages/*"] 聲明了 workspaces 中所包含的項目路徑。


package.json 配置文件增加如下配置。


開啟yarn workspaces

{"private": true,"workspaces": ["packages/*"],}lerna.json 配置文件增加如下配置。{"useWorkspaces": true,"npmClient": "yarn",}


工程初始化

對于一個已經存在的 monorepo 倉庫,使用 yarn install 安裝依賴。yarn install 會自動安裝依賴并且解決同一個倉庫之間多個package 之間的 link 問題。


yarn install 等價于 lerna bootstrap --npm-client yarn --use-workspace。


清理環境

使用 lerna clean 可以清理每個 package 下的 node_modules,但是沒有辦法清理根目錄下的 node_modules 目錄,因此,我們可以在根目錄下的 package.json 目錄的 scripts 中增加一條 clean 命令,用于清理環境。

package.json:{"clear-all": "rimraf node_modules && lerna clean -y"}


安裝依賴

安裝依賴一般分為三種情況:



安裝到workspace-root

對于一些打包工具或者代碼規范校驗工具,可以使用 yarn -W add [package] [--dev] 進行安裝,比如 typescript、eslint、cross-env、babel、rollup等。這類包一般都是一些開發依賴,比如將 ts 代碼轉換成 es5 代碼或者一些代碼校驗工具等。通過這種方式安裝的依賴包是裝在根目錄下的 node_modules 中。



給所有的 package 都安裝依賴

比如如果想給每個 package 都安裝一個 lodash 包,就可以使用 yarn workspace add lodash 給每個 package 都安裝 lodash。



給指定的某個 package 安裝依賴

通過 yarn workspace pkgA add pkgB 可以將 pkgB 作為依賴安裝到 pkgA 中,需要注意的是,如果是 packages 之間的相互安裝,安裝的時候可以指定到具體的版本號,否則安裝的時候會去npm上搜索,但是因為某個包還沒有發包出去,導致安裝失敗。


刪除包的時候只需要把上述 add 換成 remove 即可。


運行workspace的command

通過運行 yarn workspace <workspace_name> <command> 命令運行某個執行 package 下的某個 script 命令。


比如執行 pkgA 下的 build 命令,可以運行 yarn workspace pkgA run build。如果想運行 package 下的 build 命令,可以運行 yarn workspaces run build。


代碼提交

代碼編寫完畢后接下來就涉及到代碼的提交,為了規范代碼提交格式,方便自動生成 changelog,這里需要借助以下幾個工具。

commitizen && cz-conventional-changelog

commitizen 的作用主要是為了生成標準化的 commit message,符合 Angular規范。


一個標準化的 commit message 應該包含三個部分:Header、Body 和 Footer,其中的 Header 是必須的,Body 和 Footer 可以選填。

<type>(<scope>): <subject>// 空一行<body>// 空一行<footer>


Header 部分由三個字段組成:type(必需)、scope(可選)、subject(必需)


Type

type 必須是下面的其中之一:

  • feat: 增加新功能

  • fix: 修復bug

  • docs: 只改動了文檔相關的內容

  • style: 不影響代碼含義的改動,例如去掉空格、改變縮進、增刪分號

  • refactor: 代碼重構時使用,既不是新增功能也不是代碼的bud修復

  • perf: 提高性能的修改

  • test: 添加或修改測試代碼

  • build: 構建工具或者外部依賴包的修改,比如更新依賴包的版本

  • ci: 持續集成的配置文件或者腳本的修改

  • chore: 雜項,其他不需要修改源代碼或不需要修改測試代碼的修改

  • revert: 撤銷某次提交


scope

用于說明本次提交的影響范圍。scope 依據項目而定,例如在業務項目中可以依據菜單或者功能模塊劃分,如果是組件庫開發,則可以依據組件劃分。


subject

主題包含對更改的簡潔描述:

注意三點:

  1. 使用祈使語氣,現在時,比如使用      "change" 而不是      "changed" 或者      ”changes“

  2. 第一個字母不要大寫

  3. 末尾不要以.結尾


Body

主要包含對主題的進一步描述,同樣的,應該使用祈使語氣,包含本次修改的動機并將其與之前的行為進行對比。


Footer

包含此次提交有關重大更改的信息,引用此次提交關閉的issue地址,如果代碼的提交是不兼容變更或關閉缺陷,則Footer必需,否則可以省略。


使用方法:

安裝 commitizen,如果需要在項目中使用 commitizen 生成符合 AngularJS 規范的提交說明,還需要安裝 cz-conventional-changelog 適配器:

$ yarn -W add commitizen cz-conventional-changelog -D


package.json 中增加一條 script: commit: "git-cz",并且在 config 字段中指定 cz-conventional-changelog 路徑:

{"name": "root","private": true,"workspaces": ["packages/*"],"scripts": {"commit": "git-cz"},"config": {"commitizen": {"path": "cz-conventional-changelog"}},"devDependencies": {"commitizen": "^4.2.4","cz-conventional-changelog": "^3.3.0","lerna": "^4.0.0"}}


接下來就可以使用 yarn commit 來代替 git commit 進行代碼提交了。


commitlint && husky

前面我們提到,通過 commitizen && z-conventional-changelog 可以規范我們的 commit message,但是同時也存在一個問題,如果用戶不通過 yarn commit 來提交代碼,而是直接通過 git commit 命令來提交代碼,就能繞開 commit message 檢查,這是我們不希望看到的。


因此接下來我們使用 commitlint 結合 husky 來對我們的提交行為進行約束。在 git commit 提交之前使用 git 鉤子來驗證信息。提交不符合規則的信息將會被阻止提交。


安裝 commitlint 和 husky:

$ yarn -W add @commitlint/cli @commitlint/config-conventional husky -D


在工程根目錄下增加 commitlint.config.js 配置文件,指定 commitlint 的校驗配置文件:commitlint.config.js:

module.exports = { extends: ['@commitlint/config-conventional'] }


需要注意的是,如果安裝的 husky 的版本是 7.x 的,以往直接在 package.json 中 hooks 字段增加的配置項已經被廢棄了。


已經廢棄的配置:

"husky": { "hooks": { "commit-msg": "commitlint -E HUSKY_GIT_PARAMS" }},


下面在 husky 7.x 版本下進行 commit-msg 的配置。


  1. 執行 npm husky install

或者如果想在安裝后自動啟動 husky,在 package.json 的scripts中增加一條script命令:

"scripts": {"prepare": "husky install"},


prepare 是 NPM 操作生命周期中的一環,在執行 install 的時候會按生命周期順序執行相應鉤子:preinstall -> install -> postinstall -> prepublish -> preprepare -> prepare -> postprepare.


執行完畢后就會在根目錄下創建一個 .husky 目錄。


  1. 通過 husky add <file> [cmd] 指令來添加一條 hook

執行 npx husky add .husky/commit-msg 'npx --no-install commitlint --edit "$1"' 會在 .husky 下生成一個 commit-msg 的 shell 文件:

#!/bin/sh. "$(dirname "$0")/_/husky.sh"npx --no-install commitlint --edit "$1"


這樣,當我們在執行 git commit -m 'xxx' 的時候,如果提交的 commit message 不符合規范就會報如下的錯誤,只有提交信息符合提交規范才會允許代碼提交。


eslint 配置

配置 eslint 對代碼進行統一的規范校驗,配合 lint-staged 可以對已經提交的代碼進行校驗。


安裝 eslint 和 lint-stage:

$ yarn -W add eslint lint-staged @typescript-eslint/parser @typescript-eslint/eslint-plugin -D


增加 .eslintrc.js 配置文件:

module.exports = {'parser': '@typescript-eslint/parser','plugins': ['@typescript-eslint'],'rules': {'no-var': 'error',// 不能使用var聲明變量'no-extra-semi': 'error','@typescript-eslint/indent': ['error', 2],'import/extensions': 'off','linebreak-style': [0, 'error', 'windows'],'indent': ['error', 2, { SwitchCase: 1 }], // error類型,縮進2個空格'space-before-function-paren': 0, // 在函數左括號的前面是否有空格'eol-last': 0, // 不檢測新文件末尾是否有空行'semi': ['error', 'always'], // 在語句后面加分號'quotes': ['error', 'single'],// 字符串使用單雙引號,double,single'no-console': ['error', { allow: ['log', 'warn'] }],// 允許使用console.log()'arrow-parens': 0,'no-new': 0,//允許使用 new 關鍵字'comma-dangle': [2, 'never'], // 數組和對象鍵值對最后一個逗號


never參數:不能帶末尾的逗號, always參數:必須帶末尾的逗號,always-multiline多行模式必須帶逗號,單行模式不能帶逗號

'no-undef': 0},'parserOptions': {'ecmaVersion': 6,'sourceType': 'module','ecmaFeatures': {'modules': true}}};


lint-staged staged 是 Git 里的概念,表示暫存區,lint-staged 表示只檢查暫存區中的文件。


package.json 中增加如下配置:

"lint-staged": {"*.ts": ["eslint --fix","git add"]}


husky 中增加 pre-commit 校驗:

$ npx husky add .husky/pre-commit "npx --no-install lint-staged"


版本發布

通過上面的方式,嚴格 commit message 的提交規范,就可以方便地通過 lerna 完成生成 changelog、打 git tag、 更新 package.json 的 version 版本號、發布到 npm 等操作。


lerna puplish

lerna publish 的時候會做以下操作:

  • 找出從上一個版本發布以來有過變更的 package

  • 提示開發者確定要發布的版本號

  • 將所有更新過的的 package 中的 package.json 的 version 字段更新

  • 將依賴更新過的 package 的 包中的依賴版本號更新

  • 更新 lerna.json 中的 version 字段

  • 提交上述修改,并打一個 tag

  • 推送到 git 倉庫


changelog 的生成可以通過 lerna version --conventional-commits 自動生成,關于 --conventional-commits 參數,lerna 是這么描述的:

Use conventional-changelog to determine version bump and generate CHANGELOG.


在 lerna.json 增加如下配置:

{"command": {"version": {"conventionalCommits": true}},}


lerna version 會檢測從上一個版本發布以來的變動,但有一些文件的提交,我們不希望觸發版本的變動,譬如 .md 文件的修改,并沒有實際引起 package 邏輯的變化,不應該觸發版本的變更??梢酝ㄟ^ ignoreChanges 配置排除。

{"ignoreChanges": ["**/*.md"],}


配置好之后,通過 lerna publish 完成changelog生成、版本號的修改和npm發包等操作了。



總結









以上就是一個完整的基于 lerna + yarn workspace 的 monorepo 的實踐流程,里面包含了依賴包的管理、完善的工作流、統一的代碼風格、一鍵發布機制等,當然還有一些不夠完善的地方需要自己補充,比如單元測試等,有需要的可以基于上述例子進行補充完善。



官方微信公眾號

浩鯨云計算科技股份有限公司 版權所有 2003-2023

蘇ICP備10224443號-6       蘇公網安備 32011402011374號

亚洲精品免费视频_热99re6久精品国产首页青柠_精品国产专区91在线_亚洲美洲欧洲偷拍片区