pnpm對(duì)npm及yarn降維打擊詳解
正文
大家最近是不是經(jīng)常聽(tīng)到 pnpm,我也一樣。今天研究了一下它的機(jī)制,確實(shí)厲害,對(duì) yarn 和 npm 可以說(shuō)是降維打擊。
那具體好在哪里呢? 我們一起來(lái)看一下。
我們按照包管理工具的發(fā)展歷史,從 npm2 開(kāi)始講起:
npm2
用 node 版本管理工具把 node 版本降到 4,那 npm 版本就是 2.x 了。
然后找個(gè)目錄,執(zhí)行下 npm init -y,快速創(chuàng)建個(gè) package.json。
然后執(zhí)行 npm install express,那么 express 包和它的依賴(lài)都會(huì)被下載下來(lái):
展開(kāi) express,它也有 node_modules:
再展開(kāi)幾層,每個(gè)依賴(lài)都有自己的 node_modules:
也就是說(shuō) npm2 的 node_modules 是嵌套的。
這很正常呀?有什么不對(duì)么?
這樣其實(shí)是有問(wèn)題的,多個(gè)包之間難免會(huì)有公共的依賴(lài),這樣嵌套的話(huà),同樣的依賴(lài)會(huì)復(fù)制很多次,會(huì)占據(jù)比較大的磁盤(pán)空間。
這個(gè)還不是最大的問(wèn)題,致命問(wèn)題是 windows 的文件路徑最長(zhǎng)是 260 多個(gè)字符,這樣嵌套是會(huì)超過(guò) windows 路徑的長(zhǎng)度限制的。
當(dāng)時(shí) npm 還沒(méi)解決,社區(qū)就出來(lái)新的解決方案了,就是 yarn:
yarn
yarn 是怎么解決依賴(lài)重復(fù)很多次,嵌套路徑過(guò)長(zhǎng)的問(wèn)題的呢?
鋪平。所有的依賴(lài)不再一層層嵌套了,而是全部在同一層,這樣也就沒(méi)有依賴(lài)重復(fù)多次的問(wèn)題了,也就沒(méi)有路徑過(guò)長(zhǎng)的問(wèn)題了。
我們把 node_modules 刪了,用 yarn 再重新安裝下,執(zhí)行 yarn add express:
這時(shí)候 node_modules 就是這樣了:
全部鋪平在了一層,展開(kāi)下面的包大部分是沒(méi)有二層 node_modules 的:
當(dāng)然也有的包還是有 node_modules 的,比如這樣:
為什么還有嵌套呢?
因?yàn)橐粋€(gè)包是可能有多個(gè)版本的,提升只能提升一個(gè),所以后面再遇到相同包的不同版本,依然還是用嵌套的方式。
npm 后來(lái)升級(jí)到 3 之后,也是采用這種鋪平的方案了,和 yarn 很類(lèi)似:
當(dāng)然,yarn 還實(shí)現(xiàn)了 yarn.lock 來(lái)鎖定依賴(lài)版本的功能,不過(guò)這個(gè) npm 也實(shí)現(xiàn)了。
yarn 和 npm 都采用了鋪平的方案,這種方案就沒(méi)有問(wèn)題了么?
并不是,扁平化的方案也有相應(yīng)的問(wèn)題。
最主要的一個(gè)問(wèn)題是幽靈依賴(lài),也就是你明明沒(méi)有聲明在 dependencies 里的依賴(lài),但在代碼里卻可以 require 進(jìn)來(lái)。
這個(gè)也很容易理解,因?yàn)槎间伷搅寺?,那依?lài)的依賴(lài)也是可以找到的。
但是這樣是有隱患的,因?yàn)闆](méi)有顯式依賴(lài),萬(wàn)一有一天別的包不依賴(lài)這個(gè)包了,那你的代碼也就不能跑了,因?yàn)槟阋蕾?lài)這個(gè)包,但是現(xiàn)在不會(huì)被安裝了。
這就是幽靈依賴(lài)的問(wèn)題。
而且還有一個(gè)問(wèn)題,就是上面提到的依賴(lài)包有多個(gè)版本的時(shí)候,只會(huì)提升一個(gè),那其余版本的包不還是復(fù)制了很多次么,依然有浪費(fèi)磁盤(pán)空間的問(wèn)題。
那社區(qū)有沒(méi)有解決這倆問(wèn)題的思路呢?
當(dāng)然有,這不是 pnpm 就出來(lái)了嘛。
那 pnpm 是怎么解決這倆問(wèn)題的呢?
pnpm
回想下 npm3 和 yarn 為什么要做 node_modules 扁平化?不就是因?yàn)橥瑯拥囊蕾?lài)會(huì)復(fù)制多次,并且路徑過(guò)長(zhǎng)在 windows 下有問(wèn)題么?
那如果不復(fù)制呢,比如通過(guò) link。
首先介紹下 link,也就是軟硬連接,這是操作系統(tǒng)提供的機(jī)制,硬連接就是同一個(gè)文件的不同引用,而軟鏈接是新建一個(gè)文件,文件內(nèi)容指向另一個(gè)路徑。當(dāng)然,這倆鏈接使用起來(lái)是差不多的。
如果不復(fù)制文件,只在全局倉(cāng)庫(kù)保存一份 npm 包的內(nèi)容,其余的地方都 link 過(guò)去呢?
這樣不會(huì)有復(fù)制多次的磁盤(pán)空間浪費(fèi),而且也不會(huì)有路徑過(guò)長(zhǎng)的問(wèn)題。因?yàn)槁窂竭^(guò)長(zhǎng)的限制本質(zhì)上是不能有太深的目錄層級(jí),現(xiàn)在都是各個(gè)位置的目錄的 link,并不是同一個(gè)目錄,所以也不會(huì)有長(zhǎng)度限制。
沒(méi)錯(cuò),pnpm 就是通過(guò)這種思路來(lái)實(shí)現(xiàn)的。
再把 node_modules 刪掉,然后用 pnpm 重新裝一遍,執(zhí)行 pnpm install。
你會(huì)發(fā)現(xiàn)它打印了這樣一句話(huà):
包是從全局 store 硬連接到虛擬 store 的,這里的虛擬 store 就是 node_modules/.pnpm。
我們打開(kāi) node_modules 看一下:
確實(shí)不是扁平化的了,依賴(lài)了 express,那 node_modules 下就只有 express,沒(méi)有幽靈依賴(lài)。
展開(kāi) .pnpm 看一下:
所有的依賴(lài)都在這里鋪平了,都是從全局 store 硬連接過(guò)來(lái)的,然后包和包之間的依賴(lài)關(guān)系是通過(guò)軟鏈接組織的。
比如 .pnpm 下的 expresss,這些都是軟鏈接,
也就是說(shuō),所有的依賴(lài)都是從全局 store 硬連接到了 node_modules/.pnpm 下,然后之間通過(guò)軟鏈接來(lái)相互依賴(lài)。
官方給了一張?jiān)韴D,配合著看一下就明白了:
這就是 pnpm 的實(shí)現(xiàn)原理。
那么回過(guò)頭來(lái)看一下,pnpm 為什么優(yōu)秀呢?
首先,最大的優(yōu)點(diǎn)是節(jié)省磁盤(pán)空間呀,一個(gè)包全局只保存一份,剩下的都是軟硬連接,這得節(jié)省多少磁盤(pán)空間呀。
其次就是快,因?yàn)橥ㄟ^(guò)鏈接的方式而不是復(fù)制,自然會(huì)快。
這也是它所標(biāo)榜的優(yōu)點(diǎn):
相比 npm2 的優(yōu)點(diǎn)就是不會(huì)進(jìn)行同樣依賴(lài)的多次復(fù)制。
相比 yarn 和 npm3+ 呢,那就是沒(méi)有幽靈依賴(lài),也不會(huì)有沒(méi)有被提升的依賴(lài)依然復(fù)制多份的問(wèn)題。
這就已經(jīng)足夠優(yōu)秀了,對(duì) yarn 和 npm 可以說(shuō)是降維打擊。
總結(jié)
pnpm 最近經(jīng)常會(huì)聽(tīng)到,可以說(shuō)是爆火。本文我們梳理了下它爆火的原因:
npm2 是通過(guò)嵌套的方式管理 node_modules 的,會(huì)有同樣的依賴(lài)復(fù)制多次的問(wèn)題。
npm3+ 和 yarn 是通過(guò)鋪平的扁平化的方式來(lái)管理 node_modules,解決了嵌套方式的部分問(wèn)題,但是引入了幽靈依賴(lài)的問(wèn)題,并且同名的包只會(huì)提升一個(gè)版本的,其余的版本依然會(huì)復(fù)制多次。
pnpm 則是用了另一種方式,不再是復(fù)制了,而是都從全局 store 硬連接到 node_modules/.pnpm,然后之間通過(guò)軟鏈接來(lái)組織依賴(lài)關(guān)系。
這樣不但節(jié)省磁盤(pán)空間,也沒(méi)有幽靈依賴(lài)問(wèn)題,安裝速度還快,從機(jī)制上來(lái)說(shuō)完勝 npm 和 yarn。
pnpm 就是憑借這個(gè)對(duì) npm 和 yarn 降維打擊的,更多關(guān)于pnpm降維打擊npm yarn的資料請(qǐng)關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
前端算法題解leetcode36-有效的數(shù)獨(dú)示例
這篇文章主要為大家介紹了前端算法題解leetcode36-有效的數(shù)獨(dú)示例詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2022-09-09微信小程序手勢(shì)操作之單觸摸點(diǎn)與多觸摸點(diǎn)
這篇文章主要介紹了微信小程序手勢(shì)操作之單觸摸點(diǎn)與多觸摸點(diǎn)的相關(guān)資料,需要的朋友可以參考下2017-03-03使用preload預(yù)加載頁(yè)面資源時(shí)注意事項(xiàng)
本文主要介紹 preload 的使用,以及與 prefetch 的區(qū)別。然后會(huì)聊聊瀏覽器的加載優(yōu)先級(jí),大家一定要認(rèn)真看完2020-02-02使用xml2js庫(kù)進(jìn)行XML數(shù)據(jù)解析
這篇文章主要為大家介紹了使用xml2js庫(kù)進(jìn)行XML數(shù)據(jù)解析用法詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2023-09-09微信小程序 轉(zhuǎn)發(fā)功能的實(shí)現(xiàn)
這篇文章主要介紹了微信小程序 轉(zhuǎn)發(fā)功能的實(shí)現(xiàn)的相關(guān)資料,這里提供實(shí)現(xiàn)方法及實(shí)例幫助大家學(xué)習(xí)理解,需要的朋友可以參考下2017-08-08