什么是 PNPM?

什么是PNPM?

Fast, disk space efficient package manager

本質上他是一個包管理工具,和npm/yarn沒有區別,主要優勢在于

  • 包安裝速度極快
  • 磁盤空間利用效率高

使用方法:

npm i pnpm -g

優勢

一、速度快

二、高效利用磁盤空間

硬鏈接(Hard Link)
硬連接指通過索引節點來進行連接。在Linux的文件系統中,保存在磁盤分區中的文件不管是什么類型都給它分配一個編號,稱為索引節點號(Inode Index)。在Linux中,多個文件名指向同一索引節點是存在的。一般這種連接就是硬連接。硬連接的作用是允許一個文件擁有多個有效路徑名,這樣用戶就可以建立硬連接到重要文件,以防止“誤刪”的功能。其原因如上所述,因為對應該目錄的索引節點有一個以上的連接。只刪除一個連接并不影響索引節點本身和其它的連接,只有當最后一個連接被刪除后,文件的數據塊及目錄的連接才會被釋放。也就是說,文件真正刪除的條件是與之相關的所有硬連接文件均被刪除。
軟連接(Symbolic Link)
另外一種連接稱之為符號連接,也叫軟連接。軟鏈接文件有類似于Windows的快捷方式。它實際上是一個特殊的文件。在符號連接中,文件實際上是一個文本文件,其中包含的有另一文件的位置信息。

pnpm 內部使用基于內容尋址的文件系統來存儲磁盤上所有的文件:

  • 不會重復安裝同一個包。使用npm/yarn 的時候,如果100個包依賴lodash ,那么就可能安裝了100次lodash ,磁盤中就有100個地方寫入了這部分代碼。但是pnpm會只在一個地方寫入這部分代碼,后面使用會直接使用hard link
  • 即使一個包的不同版本,pnpm 也會極大程度地復用之前版本的代碼。舉個例子,比如 lodash 有 100 個文件,更新版本之后多了一個文件,那么磁盤當中并不會重新寫入 101 個文件,而是保留原來的 100 個文件的 hardlink,僅僅寫入那一個新增的文件

三、支持monorepo

monorepo 的宗旨就是用一個 git 倉庫來管理多個子項目,所有的子項目都存放在根目錄的packages目錄下,那么一個子項目就代表一個package

monorepo 管理工具lerna

項目參考:babel

四、安全

之前在使用 npm/yarn 的時候,由于 node_module 的扁平結構,如果 A 依賴 B, B 依賴 C,那么 A 當中是可以直接使用 C 的,但問題是 A 當中并沒有聲明 C 這個依賴。因此會出現這種非法訪問的情況。 但 pnpm 自創了一套依賴管理方式,很好地解決了這個問題,保證了安全性。

依賴管理

pnpm 會在全局的 store 目錄里存儲項目 node_modules 文件的 hard links 。因為這樣一個機制,導致每次安裝依賴的時候,如果是個相同的依賴,有好多項目都用到這個依賴,那么這個依賴實際上最優情況(即版本相同)只用安裝一次。

回歸一下 node_modules 結構歷史:

第一階段:npm@3 之前版本

node_modules
└─ foo
   ├─ index.js
   ├─ package.json
   └─ node_modules
      └─ bar
         ├─ index.js
         └─ package.json
  • 依賴樹層級太深,會導致 Windows 上的目錄路徑過長問題
  • 相同包在不同的依賴項中需要時,會存在多個相同副本

第二階段:npm@3 版本,扁平化處理

所有的依賴都被拍平到node_modules目錄下,不再有很深層次的嵌套關系。這樣在安裝新的包時,根據 node require 機制,會不停往上級的node_modules當中去找,如果找到相同版本的包就不會重新安裝,解決了大量包重復安裝的問題,而且依賴層級也不會太深。

node_modules
├─ foo
|  ├─ index.js
|  └─ package.json
└─ bar
   ├─ index.js
   └─ package.json

但還是存在一些問題

  • 依賴結構的不確定性
  • 扁平化算法本身的復雜性很高,耗時較長。
  • 項目中仍然可以非法訪問沒有聲明過依賴的包

這就是為什么會產生依賴結構的不確定問題,也是 lock 文件誕生的原因,無論是package-lock.json(npm 5.x才出現)還是yarn.lock,都是為了保證 install 之后都產生確定的node_modules結構。

盡管如此,npm/yarn 本身還是存在扁平化算法復雜package 非法訪問的問題,影響性能和安全。

第三階段:pnpm

由于扁平化算法的極其復雜,以及會存在多項目間相同依賴副本的情況。pnpm 在嘗試解決這些問題時,放棄了扁平化處理 node_modules 的方式。而是采用 硬鏈+軟鏈 方式。

node_modules
├─ .pnpm
|  ├─ foo@1.0.0/node_modules/foo
|  |  └─ index.js
|  └─ bar@2.0.0/node_modules/bar
├─ foo -> .pnpm/foo@1.0.0/node_modules/foo
└─ bar -> .pnpm/bar@2.0.0/node_modules/bar

node_modules 根目錄中的包只是一個符號鏈接。require('foo') 將執行 node_modules/.pnpm/foo@1.0.0/node_modules/foo/indexjs 中的文件(這里是硬鏈接),而不是 node_modules/foo/index.js 中的文件。

這種布局結構的一大好處是只有真正在依賴項中(package.json dependences)的包才能訪問

舉個??

安裝一個express 依賴,會在 node_modules 中形成這樣兩個目錄結構:

node_modules/express/...
node_modules/.pnpm/express@4.17.1/node_modules/xxx

其中第一個路徑是 nodejs 正常尋找路徑會去找的一個目錄,如果去查看這個目錄下的內容,會發現里面連個 node_modules 文件都沒有:

? express
    ? lib
      History.md
      index.js
      LICENSE
      package.json
      Readme.md

實際上這個文件只是個軟連接,它會形成一個到第二個目錄的一個軟連接(類似于軟件的快捷方式),這樣 node 在找路徑的時候,最終會找到 .pnpm 這個目錄下的內容。

其中這個 .pnpm 是個虛擬磁盤目錄,然后 express 這個依賴的一些依賴會被平鋪到 .pnpm/express@4.17.1/node_modules/ 這個目錄下面,這樣保證了依賴能夠 require 到,同時也不會形成很深的依賴層級。

在保證了 nodejs 能找到依賴路徑的基礎上,同時也很大程度上保證了依賴能很好的被放在一起。

目前不適用的場景

前面有提到關于 pnpm 的主要問題在于 symlink(軟鏈接)在一些場景下會存在兼容的問題,可以參考作者在 nodejs 那邊開的一個 discussion:https://github.com/nodejs/node/discussions/37509

在里面作者提到了目前 nodejs 軟連接不能適用的一些場景,希望 nodejs 能提供一種 link 方式而不是使用軟連接,同時也提到了 pnpm 目前因為軟連接而不能使用的場景:

  • Electron 應用無法使用 pnpm
  • 部署在 lambda 上的應用無法使用 pnpm

一些 nodejs 基礎庫不支持 symlink 的情況導致使用 pnpm 無法正常工作,不過這些庫在迭代更新之后也會支持這一特性。

總結

pnpm 方式的實現精髓

  1. 通過軟鏈的形式,使得 require 可以正常引用;同時對非真正依賴的項目做隔離(避免引用依賴的混亂)
  2. .pnpm 的存在避免了循環引用和層級過深的問題(都在其第一層)
  3. 硬鏈使得不同項目相同依賴只存在一個副本,減少磁盤空間

未來會做的一些事情

脫離 nodejs

具體可以參考 https://github.com/pnpm/pnpm/discussions/3434

  • 安裝 pnpm 的, 可以基本上脫離掉 nodejs 這個 runtime 去進行安裝使用。
  • 可以通過 pnpm 來使用不同版本的 nodejs 來去做依賴安裝,類似于 nvm 提供的功能。

目前該特性其實已經到了 beta 版本,可以參考 https://www.npmjs.com/package/@pnpm/beta 這個包。管理不同版本的 nodejs 功能可以參考 env 這個子命令: https://pnpm.io/cli/env

使用 rust 寫一些模塊

具體可以看 https://github.com/pnpm/pnpm/discussions/3419 這個 discussion 討論的內容,大概就是作者希望給 pnpm 的一些子命令提供一些 rust 的 cli wrapper 來做提升性能使用。

?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 227,967評論 6 531
  • 序言:濱河連續發生了三起死亡事件,死亡現場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機,發現死者居然都...
    沈念sama閱讀 98,273評論 3 415
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 175,870評論 0 373
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 62,742評論 1 309
  • 正文 為了忘掉前任,我火速辦了婚禮,結果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當我...
    茶點故事閱讀 71,527評論 6 407
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發上,一...
    開封第一講書人閱讀 55,010評論 1 322
  • 那天,我揣著相機與錄音,去河邊找鬼。 笑死,一個胖子當著我的面吹牛,可吹牛的內容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,108評論 3 440
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 42,250評論 0 288
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當地人在樹林里發現了一具尸體,經...
    沈念sama閱讀 48,769評論 1 333
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 40,656評論 3 354
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發現自己被綠了。 大學時的朋友給我發了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 42,853評論 1 369
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 38,371評論 5 358
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質發生泄漏。R本人自食惡果不足惜,卻給世界環境...
    茶點故事閱讀 44,103評論 3 347
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 34,472評論 0 26
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 35,717評論 1 281
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 51,487評論 3 390
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 47,815評論 2 372

推薦閱讀更多精彩內容

  • 一、顯示npm install 詳細信息 按照如下設置后,可以在npm install 后查看每一個步驟,方便查看...
    該帳號已被查封_才怪閱讀 4,235評論 0 4
  • 一、概念介紹 Vue.js和React.js分別是目前國內和國外最火的前端框架,框架跟類庫/插件不同,框架是一套完...
    劉遠舟閱讀 1,076評論 0 0
  • 1.npm是Node官方提供的包管理工具,他已經成了Node包的標準發布平臺,用于Node包的發布、傳播、依賴控制...
    shuo_fd6c閱讀 2,599評論 0 0
  • 網上的 npm 教程主要都在講怎么安裝、配置和使用 npm,卻不告訴新人「為什么要使用 npm」。今天我就來講講這...
    向一路北閱讀 1,107評論 1 1
  • 1 Webpack 1.1 概念簡介 1.1.1 WebPack是什么 1、一個打包工具 2、一個模塊加載工具 3...
    Kevin_Junbaozi閱讀 6,697評論 0 16