<kbd id="afajh"><form id="afajh"></form></kbd>
<strong id="afajh"><dl id="afajh"></dl></strong>
    <del id="afajh"><form id="afajh"></form></del>
        1. <th id="afajh"><progress id="afajh"></progress></th>
          <b id="afajh"><abbr id="afajh"></abbr></b>
          <th id="afajh"><progress id="afajh"></progress></th>

          lerna 還是 pnpm + changesets?monorepo 工具核心就看這三個功能

          共 5717字,需瀏覽 12分鐘

           ·

          2023-05-10 07:31

          monorepo 是多個包在同一個項目中管理的方式,是很流行的項目組織形式。

          主流的開源包基本都是用 monorepo 的形式管理的。

          為什么用 monorepo 也很容易理解:

          比如 babel 分為了 @babel/core、@babel/cli、@babel/parser、@babel/traverse、@babel/generator 等一系列包。

          如果每個包單獨一個倉庫,那就有十多個 git 倉庫,這些 git 倉庫每個都要單獨來一套編譯、lint、發(fā)包等工程化的工具和配置,重復十多次。

          工程化部分重復還不是最大的問題,最大的問題還是這三個:

          1. 一個項目依賴了一個本地還在開發(fā)的包,我們會通過 npm link 的方式把這個包 link 到全局,然后再 link 到那個項目的 node_modules 下。

          npm link 的文檔是這么寫的:

          e469127a7811b46ceea25de35783ccfb.webp

          就是把代碼 link 到全局再 link 到另一個項目,這樣只要這個包的代碼改了,那個項目就可以直接使用最新的代碼。

          如果只是一個包的話,npm link 還是方便的。但現在有十幾個包了,這樣來十多次就很麻煩了。

          1. 需要在每個包里執(zhí)行命令,現在也是要分別進入到不同的目錄下來執(zhí)行十多次。最關鍵的是有一些包需要根據依賴關系來確定執(zhí)行命令的先后順序。

          2. 版本更新的時候,要手動更新所有包的版本,如果這個包更新了,那么依賴它的包也要發(fā)個新版本才行。

          這也是件麻煩的事情。

          因為這三個問題:npm link 比較麻煩、執(zhí)行命令比較麻煩、版本更新比較麻煩,所以就有了對 monorepo 的項目組織形式和工具的需求。

          比如主流的 monorepo 工具 lerna,它描述自己解決的三個大問題也是這個:

          cdc74372c3d3dbf6467ae4655df06721.webp

          也就是說,把理清了這三個點,就算是掌握了 monorepo 工具的關鍵了。

          我們分別來看一下:

          npm link 的流程實際上是這樣的:

          0c339151beac07a617dae14092b1c4a1.webp

          npm 包先 link 到全局,再 link 到另一個項目的 node_modules。

          而 monorepo 工具都是這樣做的:

          ae5c8feda7e43cbf07114f1b1bddbea4.webp

          比如一個 monorepo 項目下有 a、b、c 三個包,那么 monorepo 工具會把它們 link 到父級目錄的 node_modules。

          node 查找模塊的時候,一層層往上查找,就都能找到彼此了,就完成了 a、b、c 的相互依賴。

          比如用 lerna 的 demo 項目試試:

                
                git?clone?https://github.com/lerna/getting-started-example.git

          下載下來是這樣的結構:

          5d86cf7703c490137674c123e9c02f92.webp

          執(zhí)行 npm install,在根目錄的 node_modules 下就會安裝很多依賴。

          包括我們剛說的 link 到根 node_modules 里的包:

          078b61731552a3b87f8c7a315e4de2ff.webp4daa1ece2b22ef2593cd4413e0543328.webpac26a4400068a726987b287f86e5ee99.webp

          這個箭頭就是軟鏈接文件的意思。

          底層都是系統提供的 ln -s 的命令。

          比如我執(zhí)行

                
                ln?-s?package.json?package2.json

          那就是創(chuàng)建一個 package2.json 的軟連接文件,內容和 package.json 一樣。

          這倆其實是一個文件,一個改了另一個也就改了:

          270b97474d51400ef5152c670fbb1c13.webp

          原理都是軟連接,只不過 npm link 的那個和 monorepo 這個封裝的有點區(qū)別。

          這種功能本來是 lerna 先實現的,它提供了 lerna bootstrap 來完成這種 link:

          2c7c3fbce418925e2c9111dc7758bc98.webp

          只不過后來 npm、yarn、pnpm 都內置了這個功能,叫做 workspace。就不再需要 lerna 這個 bootstrap 的命令了。

          直接在 package.json 里配置 workspace 的目錄:

          d352c250721e3cf1a64b73e8b434da55.webp

          然后 npm install,就會完成這些 package 的 link。

          而包與包之間的依賴,workspace 會處理,本地開發(fā)的時候只需要寫 * 就好,發(fā)布這個包的時候才會替換成具體的版本。

          8ce73ecee4738dd32d54598720a4629b.webp

          這里用的是 npm workspace:

          724b9447240af7adf2482335cbbe4cd2.webp

          它所解決的問題正如我們分析的:

          b405daf4c1c9e2c8e605776bb7cd00c9.webp

          在 npm install 的時候自動 link。

          yarn workspace 也是一樣的方式:

          e4f043ee699c14bc2821f6a9ec17deaa.webp

          pnpm 有所不同,是放在一個 yaml 文件里的:

          db79c3bee3786d4aab2dc39b8884bff8.webp

          此外,yarn 和 pnpm 支持 workspace 協議,需要把依賴改為這樣的形式:

          d75e63727375976fef3bf37400d12f5a.webp

          這樣查找依賴就是從 workspace 里查找,而不是從 npm 倉庫了。

          5f668c90a828c911cbc1d83964801e99.webp

          總之,不管是 npm workspace、yarn workspace 還是 pnpm workspace,都能達到在 npm install 的時候自動 link 的目的。

          回過頭來再來看 monorepo 工具的第二大功能:執(zhí)行命令

          在剛才的 demo 項目下執(zhí)行

                
                lerna?run?build

          輸出是這樣的:

          1e11099c5831882e93f764d0f010e98b.webp

          lerna 會按照依賴的拓撲順序來執(zhí)行命令,并且合并輸出執(zhí)行結果。

          比如 remixapp 依賴了 header 和 footer 包,所以先在 footer 和 header 下執(zhí)行,再在 remixapp 下執(zhí)行。

          當然,npm workspace、yarn workspace、pnpm workspace 也是提供了多包執(zhí)行命令的支持的。

          npm workspace 執(zhí)行剛才的命令是這樣的:

                
                npm?exec?--workspaces?--?npm?run?build

          可以簡寫為:

                
                npm?exec?-ws?--?npm?run?build
          deb897c8b6333070e1dd0faf484a7096.webp

          也可以單獨執(zhí)行某個包下執(zhí)行:

                
                npm?exec?--workspace?header?--workspace?footer?--?npm?run?build

          可以簡寫為:

                
                npm?exec?-w?header?-w?footer??--?npm?run?build

          只不過不支持拓撲順序。

          yarn workspace 可以執(zhí)行:

                
                yarn?workspaces?run?build
          d965c189b1148dab3f0268d84cd819d9.webp

          但也同樣不支持拓撲順序。

          我們再來試試 pnpm workspace。

          npm workspace 和 yarn workspace 只要在 package.json 里聲明 workspaces 就可以。

          但 pnpm workspace 要聲明在 pnpm-workspaces.yaml 里:

          1dc39ab87ff93adc2f0ec9f066013fdf.webp

          pnpm 在 workspace 執(zhí)行命令是這樣的:

                
                pnpm?exec?-r?pnpm?run?build

          -r 是遞歸的意思:

          e27151825934b6bc07388177334576ad.webp

          關鍵是 pnpm 是支持選擇拓撲排序,然后再執(zhí)行命令的:

          3ec97a842b539fe9332022e64fe1e451.webp

          有時候命令有執(zhí)行先后順序的要求的時候就很有用了。

          總之,npm、yarn、pnpm 都和 lerna 一樣支持 workspace 下命令的執(zhí)行,而且 pnpm 和 lerna 都是支持拓撲排序的。

          再來看最后一個 monorepo 工具的功能:版本管理和發(fā)布。

          有個工具叫做 changesets 是專門做這個的,我們看下它能做啥就好了。

          執(zhí)行 changeset init:

                
                npx?changeset?init

          執(zhí)行之后會多這樣一個目錄:

          7fe2d67765e7b061114b636d271743b6.webp

          然后添加一個 changeset。

          什么叫 changeset 呢?

          就是一次改動的集合,可能一次改動會涉及到多個 package,多個包的版本更新,這合起來叫做一個 changeset。

          我們執(zhí)行 add 命令添加一個 changeset:

                
                npx?changeset?add

          會讓你選一個項目:

          210b380d11d222fa349874e75d5b2bf0.webp

          哪個是 major 版本更新,哪個是 minor 版本更新,剩下的就是 pacth 版本更新。

          a1591373a1cb59e1a6686953f0a62185.webp

          1.2.3 ?這里面 1 就是 major 版本、2 是 minor 版本、3 是 patch 版本。

          之后會讓你輸入這次變更的信息:

          6d566e4375f45bfcd7ad545b0e9588ff.webp

          然后你就會發(fā)現在 .changeset 下多了一個文件記錄著這次變更的信息:

          d6860198ff4c0a16c9ea9bd7de638cb0.webp

          然后你可以執(zhí)行 version 命令來生成最終的 CHANGELOG.md 還有更新版本信息:

                
                npx?changeset?version

          之后那些臨時的 changeset 文件就消失了:

          8bf94d743b1a9a28cddc92f02958560c.webp

          更改的包下都多了 CHANGELOG.md 文件:

          11e1bdaa8ac85e4630c8ae88edb0a20e.webpae28a3f636cc6e3a7f80412fdee35e72.webp

          并且都更新了版本號:

          27b1e3c741e0b30a495de5acaa2cadcd.webpc693330507398241dcbd0555eecae3f6.webp

          而且 remixapp 這個包雖然沒有更新,但是因為依賴的包更新了,所以也更新了一個 patch 版本:

          11eaa6d9b441878fbeec44dd80d365fb.webpe8c0adcb0b0912d62a53b243de893dad.webp

          這就是 changeset 的作用。

          如果沒有這個工具呢?

          你要自己一個個去更新版本號,而且你還得分析依賴關系,知道這個包被哪些包用到了,再去更改那些依賴這個包的包的版本。

          就很麻煩。

          這就是 monorepo 工具的版本更新功能。

          更新完版本自然是要 publish 到 npm 倉庫的。

          執(zhí)行 changeset publish 命令就可以,并且還會自動打 tag:

          8395aa5d5a1bdb9e3a7c9afc1a309443.webp

          如果你不想用 changeset publish 來發(fā)布,想用 pnpm publish,那也可以用 changeset 來打標簽:

          39da3d58a8ab27b0abef5ccd954baf59.webp
                
                npx?changeset?tag
          4a6931f7f80a0a38600320dfb68ee35f.webp

          這就是 monorepo 工具的版本更新和發(fā)布的功能。

          lerna 是自己實現的一套,但是用 pnpm workspace + changeset 也完全可以做到。

          回過頭來看下這三個功能:

          96dfd8901a3d6cf0c6b447313f3a0699.webp

          不同包的自動 link,npm workspace、yarn workspace、pnpm workspace 都可以做到,而 lerna bootstrap 也廢棄了,改成基于 workspace。

          執(zhí)行命令這個也是都可以,只不過 lerna 和 pnpm workspace 都支持拓撲順序執(zhí)行命令。

          版本更新和發(fā)布這個用 changeset 也能實現,用 lerna 的也可以。

          整體看下來,似乎沒啥必要用 lerna 了,用 pnpm workspace + changesets 就完全能覆蓋這些需求。

          那用 lerna 的意義在哪呢?

          雖然功能上沒啥差別,但性能還是有差別的。

          lerna 還支持命令執(zhí)行緩存,再就是可以分布式執(zhí)行任務。

          執(zhí)行 lerna add-caching 來添加緩存的支持:

          0ed1a4eee991a53be85f79647aac002e.webp

          指定 build 和 test 命令是可以緩存的,輸出目錄是 dist。

          那當再次執(zhí)行的時候,如果沒有變動,lerna 就會直接輸出上次的結果,不會重新執(zhí)行命令。

          下面分別是第一次和第二次執(zhí)行:

          2836a797cc73b124f206855360d6d6a6.webp

          至于分布式執(zhí)行任務這個,是 nx cloud 的功能,貌似是可以在多臺機器上跑任務。

          所以綜合看下來,lerna 在功能上和 pnpm workspace + changesets 沒啥打的區(qū)別,但是在性能上更好點。

          如果項目比較大,用 lerna 還是不錯的,否則用 pnpm workspace + changesets 也完全夠用了。

          總結

          monorepo 是在一個項目中管理多個包的項目組織形式。

          它能解決很多問題:工程化配置重復、link 麻煩、執(zhí)行命令麻煩、版本更新麻煩等。

          lerna 在文檔中說它解決了 3 個 monorepo 最大的問題:

          • 不同包的自動 link
          • 命令的按順序執(zhí)行
          • 版本更新、自動 tag、發(fā)布
          3844cf3a63ab3a1ff408b658735f4414.webp

          這三個問題是 monorepo 的核心問題。

          第一個問題用 pmpm workspace、npm workspace、yarn workspace 都可以解決。

          第二個問題用 pnpm exec 也可以保證按照拓撲順序執(zhí)行,或者用 npm exec 或者 yarn exec 也可以。

          第三個問題用 changesets 就可以做到。

          lerna 在功能上和 pnpm workspace + changesets 并沒有大的差別,主要是它做了命令緩存、分布式執(zhí)行任務等性能的優(yōu)化。

          總之,monorepo 工具的核心就是解決這三個問題。



          瀏覽 132
          點贊
          評論
          收藏
          分享

          手機掃一掃分享

          分享
          舉報
          評論
          圖片
          表情
          推薦
          點贊
          評論
          收藏
          分享

          手機掃一掃分享

          分享
          舉報
          <kbd id="afajh"><form id="afajh"></form></kbd>
          <strong id="afajh"><dl id="afajh"></dl></strong>
            <del id="afajh"><form id="afajh"></form></del>
                1. <th id="afajh"><progress id="afajh"></progress></th>
                  <b id="afajh"><abbr id="afajh"></abbr></b>
                  <th id="afajh"><progress id="afajh"></progress></th>
                  俺去俺来也www色官网免费的 | 日韩资源网| 97热热| 一级A片操逼 | aaa亚洲天堂 |