聊聊前端包管理器對比Npm、Yarn和Pnpm
前言
本文將從前端包管理器的發(fā)展開始說起,然后對比npm、yarn和pnpm。
沒有包管理器
依賴(dependency)是別人為了解決一些問題而寫好的代碼,即我們常說的第三方包或三方庫。
一個(gè)項(xiàng)目或多或少的會有一些依賴,而你安裝的依賴又可能有它自己的依賴。
比如,你需要寫一個(gè)base64編解碼的功能,你可以自己寫,但為什么要自己造輪子呢?大多數(shù)情況下,一個(gè)可靠的第三方依賴經(jīng)過多方測試,兼容性和健壯性會比你自己寫的更好。
項(xiàng)目中的依賴,可以是一個(gè)完整的庫或者框架,比如react或vue;可以是一個(gè)很小的功能,比如日期格式化;也可以是一個(gè)命令行工具,比如eslint。
如果沒有現(xiàn)代化的構(gòu)建工具,即包管理器,你需要用<script>標(biāo)簽來引入依賴。
此外,如果你發(fā)現(xiàn)了一個(gè)比當(dāng)前使用的依賴更好的庫,或者你使用的依賴發(fā)布了更新,而你想用最新版本,在一個(gè)大的項(xiàng)目中,這些版本管理、依賴升級將是讓人頭疼的問題。
于是包管理器誕生了,用來管理項(xiàng)目的依賴。
它提供方法給你安裝依賴(即安裝一個(gè)包),管理包的存儲位置,而且你可以發(fā)布自己寫的包。
npm v1-v2
初代npm(Node.js Package Manager)隨著Node.js的發(fā)布出現(xiàn)了。
它的文件結(jié)構(gòu)是嵌套的:
這會導(dǎo)致3個(gè)問題:
1、node_modules體積過大(大量重復(fù)的包被安裝)
2、node_modules嵌套層級過深(會導(dǎo)致文件路徑過長的問題)
3、模塊實(shí)例不能共享
yarn & npm v3
這個(gè)版本yarn和npm v3帶來了扁平化依賴管理:
扁平化處理時(shí),比如安裝A,A依賴B和C,C依賴D和E,就把A~E全部放到node_modules目錄下,從而解決上個(gè)版本中node_modules嵌套層級過深的問題。
在install安裝時(shí),會不停的往上級node_modules中尋找,如果找到同樣的包,就不再重復(fù)安裝,從而解決了大量包被重復(fù)安裝的問題。
但是扁平化帶來了新的問題:
1、依賴結(jié)構(gòu)的不確定性
2、扁平化算法本身復(fù)雜性很高,耗時(shí)較長
3、項(xiàng)目中仍然可以非法訪問沒有聲明過依賴的包
對于問題1,比如B和C都依賴了F,但是依賴的F版本不一樣:
依賴結(jié)構(gòu)的不確定性表現(xiàn)是扁平化的結(jié)果不確定,以下2種情況都有可能,取決于package.json中B和C的位置。
于是出現(xiàn)yarn.lock(npm5才有package-lock.json),來保證install后產(chǎn)生確定的依賴結(jié)構(gòu)。但這并不能完全解決問題,node_modules中依然存在各種不同版本的F,而這可能導(dǎo)致各種情況的編譯報(bào)錯(cuò),以及安裝滿,占磁盤空間。
對于問題3,package.json中我們只聲明了A,B~F都是因?yàn)楸馄交幚聿欧诺胶虯同級的node_modules下,理論上在項(xiàng)目中寫代碼時(shí)只可以使用A,但實(shí)際上B~F也可以使用,由于扁平化將沒有直接依賴的包提升到node_modules一級目錄,Node.js沒有校驗(yàn)是否有直接依賴,所以項(xiàng)目中可以非法訪問沒有聲明過依賴的包。
這會產(chǎn)生兩個(gè)問題:
- B~F中的包升級后,項(xiàng)目可能出問題
- 額外的管理成本(比如協(xié)作時(shí)別人運(yùn)行一次npm install后項(xiàng)目依舊跑不起來)
pnmp
pnpm(Performance npm)的作者Zoltan Kochan發(fā)現(xiàn) yarn 并沒有打算去解決上述的這些問題,于是另起爐灶,寫了全新的包管理器。
pnpm復(fù)刻了npm所有的命令,所以使用方法和npm一樣,并且在安裝目錄結(jié)構(gòu)上做了優(yōu)化,特點(diǎn)是善用鏈接,且由于鏈接的優(yōu)勢,大多數(shù)情況下pnpm的安裝速度比yarn和npm更快。
比如安裝A,A依賴了B:
1、安裝依賴
A和B一起放到.pnpm中(和上面相比,這里沒有耗時(shí)的扁平化算法)。
另外A@1.0.0下面是node_modules,然后才是A,這樣做有兩點(diǎn)好處:
- 允許包引用自身
- 把包和它的依賴攤平,避免循環(huán)結(jié)構(gòu)
2、處理間接依賴
A平級目錄創(chuàng)建B,B指向B@1.0.0下面的B。
3、處理直接依賴
頂層node_modules目錄下創(chuàng)建A,指向A@1.0.0下的A。
對于更深的依賴,比如A和B都依賴了C:
總結(jié)
如果你想更快的速度,更小的空間,你應(yīng)該選擇pnpm;
如果你要用Monorepo,你可以用yarn或pnpm;
如果是node項(xiàng)目,你應(yīng)該用npm,因?yàn)檫@是node官方推薦的,而且yarn不支持node5+;
對于npm項(xiàng)目,如果你擔(dān)心項(xiàng)目的安全性,你可以考慮用yarn替換npm。
參考
1、為什么現(xiàn)在我更推薦 pnpm 而不是 npm/yarn?(
https://www.cnblogs.com/cangqinglang/p/14448329.html)
2、Node.js 包管理器發(fā)展史(
https://wxsm.space/2021/npm-history/)
3、JavaScript package managers compared: Yarn, npm, or pnpm?(
https://blog.logrocket.com/javascript-package-managers-compared/)
3、pnpm官網(wǎng)(https://pnpm.io/)