[pnpm] pnpm 與 npm/yarn 的對比

来源:https://www.cnblogs.com/feixianxing/p/18359597/node-package-manager-compare-pnpm-vs-npm-and-yarn
-Advertisement-
Play Games

本文比較了NPM、Yarn 和 pnpm 三種包管理工具的特點,重點分析了它們在安裝速度、依賴管理、磁碟空間使用、依賴衝突處理等方面的差異,重點介紹了pnpm的依賴組織結構。 ...


JavaScript 應用程式通常依賴於許多外部庫,這些依賴項通常通過包管理器來管理。預設情況下,Node.js 使用 NPM 作為包管理器。

由於早期的 NPM 存在各種不足,社區後來開發了 Yarn 和 pnpm 作為替代品。

如果要使用 Yarn 和 pnpm,則需要先通過 NPM 進行安裝。

早期 NPM 的不足

image-20240814145147621

  1. 依賴樹過深

    在 NPM 3.0 之前,NPM 使用了嵌套依賴樹的結構。這意味著如果一個項目的多個依賴項需要同一個包的不同版本,NPM 會在每個依賴項的目錄中重覆安裝該包。這種結構會導致 node_modules 目錄非常深,特別是在 Windows 系統中,這可能導致路徑長度限制的問題。

  2. 重覆安裝和磁碟空間浪費

    每次安裝包時都會重新從頭開始解決依賴關係,並逐個下載和安裝包。即使是已經安裝過的包,也可能會再次下載,而沒有利用緩存機制。這種重覆安裝的策略會導致安裝十分緩慢。

  3. 依賴版本不確定

    在早期版本的 NPM 中,沒有類似 yarn.lockpackage-lock.json 這樣的鎖文件。這意味著即使 package.json 中指定了版本範圍(例如^1.0.0這種表示可以接受一個範圍的版本),依賴關係的解析和安裝仍然是動態的,可能會因為時間或網路狀態的不同而導致不同的版本被安裝。

  4. 扁平化依賴樹

    NPM 在 3.0 版本引入了扁平化依賴樹,以解決早期版本中嵌套依賴樹帶來的問題,但是扁平化依賴帶來了新的問題。

    • 依賴衝突:扁平化依賴樹的設計將所有依賴項都安裝在項目的根 node_modules 目錄中,這意味著多個包可能會共用同一個依賴項的版本。如果不同的包需要不同版本的相同依賴項,就可能會發生衝突。
    • 幽靈依賴:依賴的依賴被平鋪在根node_modules目錄中,這意味著即使應用的package.json中沒有聲明的依賴,也可以被引入並使用。這種現象會導致依賴關係和依賴版本的不明確。

    image-20240814151136457

    圖中的虛線就代表幽靈依賴,也叫隱式依賴

    依賴 E 原本是 B 的依賴,但是被扁平化後提升到 node_modules 頂層。

    這個 E 沒有被顯式地在 package.json 中聲明,但是結合 node.js 的模塊解析機制可知這個依賴是可以被 Project 引入的。

    這種 意料之外 的依賴關係會使得項目難以維護。

Yarn

Yarn的提出是為瞭解決 NPM 的不足,它具有以下特點:

  1. 確定性安裝

    Yarn 引入了 yarn.lock鎖文件,明確了依賴的版本。

  2. 更快更小

    Yarn 通過並行下載以及引入緩存機制來加快安裝速度,並且由於緩存的存在,在離線狀態下也可以安裝已緩存過的依賴。

  3. 扁平化依賴結構

    減少了路徑深度,提高了依賴解析的速度。解決了依賴衝突問題:Yarn 會通過將不同版本的依賴項放在各自子目錄的 node_modules 中來解決衝突,而不是強制將所有依賴都安裝在頂層。

    image-20240814151859723

  4. 可以通過配置 workspaces 支持 monorepo。

不足

  • 沒有解決幽靈依賴的問題;
  • workspaces 配置較繁瑣。

pnpm

image-20240814142111601

pnpm的特點:

  1. 節省磁碟空間

    npm 和 Yarn 會在每個項目的 node_modules 目錄中為所有依賴項存儲完整的文件副本。如果有多個項目依賴相同的包,那麼這些包會被重覆存儲

    pnpm 使用中心化的 store 統一存儲安裝的包,項目內的依賴通過鏈接指向 store 中的依賴。如果有多個項目依賴相同的包,都指向 store 中單一的包。

  2. 安裝速度更快

    pnpm npm/yarn/...
    1. 依賴解析。 倉庫中沒有的依賴都被識別並獲取到倉庫。
    2. 目錄結構計算。 node_modules 目錄結構是根據依賴計算出來的。
    3. 鏈接依賴項。 所有以前安裝過的依賴項都會直接從倉庫中獲取並鏈接到 node_modules
    1. 解析所有依賴。
    2. 獲取所有依賴。
    3. 將所有依賴寫入node_modules
    pnpm 安裝過程的插圖。 所有的包會儘快解吸、獲取和硬鏈接。 一個 Yarn Classic 或 npm 這樣的包管理器如何安裝依賴項的示意圖。

    pnpm 的中心化store可以更大程度地復用依賴包,使得安裝依賴這一步驟更快完成。

  3. 支持 monorepo,配置比起 yarn 來說相對簡單,並且得益於 pnpm 的特性,安裝依賴很快。

  4. 非扁平化的 node_modules

    上文說到 yarn 和 npm 為瞭解決路徑過長、依賴管理複雜等問題,將依賴進行扁平化管理。但是也帶來了幽靈依賴等新問題。

    pnpm 的創新點在於提出了 基於符號鏈接的非扁平化 node_modules 結構,解決了幽靈依賴問題。

    硬鏈接和軟鏈接

    在 Linux 操作系統中,每一個文件對應一個 inode(索引節點)。鏈接是一種在共用文件和訪問它的用戶的若幹目錄項之間建立聯繫的一種方法。

    image-20240814164425085
    • 硬鏈接是文件的別名,和源文件指向同一個 inode。即硬鏈接和源文件是同一個文件
    • 軟連接也叫符號鏈接,是一種特殊的文件類型,其中包含對另一個文件的引用。軟鏈接可以看作是對一個文件的間接指針,類似於 Windows 操作系統下的 快捷方式即軟鏈接和源文件是不同文件

    在 Windows 中也有軟硬鏈接的概念,在 cmd 中通過 mklink指令創建鏈接:

    • 硬鏈接:

      mklink /H link_name target_file
      
    • 軟鏈接

      mklink link_name target_file
      

    pnpm的node_modules結構

    文件結構示例:comparing-node-modules/pnpm5-example at master · zkochan/comparing-node-modules (github.com)

    pnpm將實際的依賴文件都安裝到全局store中,在項目中的node_modules文件夾內通過創建鏈接來使用store中的依賴。

    與 yarn 和 npm 直接將所有依賴平鋪在 node_modules 中的做法不同,pnpm 在 node_modules 中創建了一個 .pnpm文件夾,再將所有依賴都平鋪在這個文件夾中。這樣 node.js 的模塊解析演算法就無法引入非頂層依賴了,故解決了幽靈依賴問題。

    .pnpm 中的依賴通過軟鏈接建立依賴之間的父子關係,並通過硬鏈接指向實際存在於全局store中的依賴包。

    在 package.json 中顯式聲明的依賴會通過軟鏈接提升到 node_modules 文件夾下,因此 node.js 可以正常解析 package.json 中聲明的依賴。

    .pnpm 中,依賴通過.pnpm/<name>@<version>/node_modules/<name>的形式進行記錄,可以看到同一個包的不同版本會被分開記錄

    image-20240814181732826

    如上圖,項目中只有 express 這一個依賴,而 express 有許多子依賴,這裡只列舉了 qs 這一個依賴。

    可以觀察到,這種基於鏈接的 node_modules 結構實現了:

    1. 項目的 node_modules 只能解析到 package.json 中顯式聲明的依賴,解決了幽靈依賴問題
    2. 所有依賴都被平鋪在 .pnpm 文件夾中,不會導致過長的文件路徑
    3. 實際的依賴被安裝在全局的store中,項目中僅通過硬鏈接進行關聯,節省了磁碟空間
    4. 觀察到express和它的依賴同屬於一個文件夾層級(圖中藍色區域),express 所有的依賴都軟鏈至了 node_modules/.pnpm/ 中的對應目錄。 把 express 的依賴放置在同一級別避免了迴圈的軟鏈。

現在的 NPM

yarn 和 pnpm 屬於社區產物,NPM 作為官方的包管理器,一直在吸收社區好物的優點。

現在的 NPM 也有了鎖文件來明確依賴的版本,並且也通過使用緩存、改進依賴解析演算法等手段加速了安裝。

NPM 在 7.0 版本之後也支持配置 monorepo 了,可以在 package.json 中直接配置,但是只支持一些簡單的功能。yarn 則提供了插件系統。

總結

特點 NPM Yarn pnpm
安裝速度 較慢 較快 大部分情況下比 Yarn 塊
依賴管理 直接安裝到 node_modules 通過緩存加速安裝 中心化 store,依賴通過符號鏈接安裝
磁碟空間使用 中等 最低,通過去重和鏈接機制
依賴衝突處理 容易出現衝突 通過鎖文件和解析依賴減少衝突 嚴格隔離各依賴版本,減少衝突
鎖文件 package-lock.json yarn.lock pnpm-lock.yaml
幽靈依賴問題 可能發生 可能發生 嚴格依賴樹,避免幽靈依賴
monorepo支持 基礎支持 功能豐富,包含插件系統 高效的工作空間管理,模塊共用更優化
安裝一致性 可能由於緩存和平臺差異而不一致 高,一致性較好 更高,通過全局硬鏈接機制確保一致性

性能對比圖像來自 pnpm 官方文檔:Benchmarks of JavaScript Package Managers | pnpm中文文檔 | pnpm中文網

Graph of the alotta-files results


您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • title: 使用 onNuxtReady 進行非同步初始化 date: 2024/8/16 updated: 2024/8/16 author: cmdragon excerpt: 摘要:本文詳細介紹了Nuxt.js框架中的onNuxtReady函數用途、使用場景及其實現步驟,並通過集成分析庫的示例 ...
  • 在做項目的時候,Web端需要適配移動端,且部分參數需要判斷對應系統或瀏覽器。 在IOS中,因為高版本的系統Safari瀏覽器採用了Mac的內核,所以導致普通的判斷無法識別到對應系統。(userAgent.match(/(Macintosh)/) && navigator.maxTouchPoints ...
  • Mall-Vue —— 一個基於 VUE + VUEX + iView 做的一個電商網站前端項目。這個項目是一個純前端項目,實現了首頁、商品頁面、購物車等大部分常用的電商網站頁面功能。 ...
  • 本文介紹了記憶體管理的基礎知識,重點分析了棧區與堆區的區別,並詳細討論了V8引擎的記憶體管理機制,包括垃圾回收策略和優化技術。文章通過實例代碼展示了堆區和棧區的記憶體變化,並探討了v8如何通過並行、增量和併發技術優化垃圾回收性能。 ...
  • 本文主要內容: screenX 和 screenY, clientX 和 clientY / x 和 y pageX 和 pageY layerX 和 layerY 在處理滑鼠事件(MouseEvent)時,瞭解不同的坐標系是非常重要的。常見的坐標系包括屏幕坐標(screen)、客戶端坐標(clie ...
  • 一、載入問題 用高德地圖做了個進京證路線規劃的功能,官網也是有 React 代碼示例。但是吧,這個Demo有問題,地圖是能載入成功,但是其他功能再用 map 這個變數肯定不行,果不其然是null,處理也簡單,把公共變數都管理起來就行了。 const [map, setMap] = useState( ...
  • title: 使用 onBeforeRouteUpdate 組合式函數提升應用的用戶體驗 date: 2024/8/15 updated: 2024/8/15 author: cmdragon excerpt: 摘要:本文介紹如何在Nuxt 3開發中使用onBeforeRouteUpdate組合式函 ...
  • 歐陽堅持每周更新一篇高質量文章半年後的收益:收入1380.27元、電子書一本、微信技術群418人、微信好友459人、文章38篇 ...
一周排行
    -Advertisement-
    Play Games
  • 前言 推薦一款基於.NET 8、WPF、Prism.DryIoc、MVVM設計模式、Blazor以及MySQL資料庫構建的企業級工作流系統的WPF客戶端框架-AIStudio.Wpf.AClient 6.0。 項目介紹 框架採用了 Prism 框架來實現 MVVM 模式,不僅簡化了 MVVM 的典型 ...
  • 先看一下效果吧: 我們直接通過改造一下原版的TreeView來實現上面這個效果 我們先創建一個普通的TreeView 代碼很簡單: <TreeView> <TreeViewItem Header="人事部"/> <TreeViewItem Header="技術部"> <TreeViewItem He ...
  • 1. 生成式 AI 簡介 https://imp.i384100.net/LXYmq3 2. Python 語言 https://imp.i384100.net/5gmXXo 3. 統計和 R https://youtu.be/ANMuuq502rE?si=hw9GT6JVzMhRvBbF 4. 數 ...
  • 本文為大家介紹下.NET解壓/壓縮zip文件。雖然解壓縮不是啥核心技術,但壓縮性能以及進度處理還是需要關註下,針對使用較多的zip開源組件驗證,給大家提供個技術選型參考 之前在《.NET WebSocket高併發通信阻塞問題 - 唐宋元明清2188 - 博客園 (cnblogs.com)》講過,團隊 ...
  • 之前寫過兩篇關於Roslyn源生成器生成源代碼的用例,今天使用Roslyn的代碼修複器CodeFixProvider實現一個cs文件頭部註釋的功能, 代碼修複器會同時涉及到CodeFixProvider和DiagnosticAnalyzer, 實現FileHeaderAnalyzer 首先我們知道修 ...
  • 在軟體行業,經常會聽到一句話“文不如表,表不如圖”說明瞭圖形在軟體應用中的重要性。同樣在WPF開發中,為了程式美觀或者業務需要,經常會用到各種個樣的圖形。今天以一些簡單的小例子,簡述WPF開發中幾何圖形(Geometry)相關內容,僅供學習分享使用,如有不足之處,還請指正。 ...
  • 在 C# 中使用 RabbitMQ 通過簡訊發送重置後的密碼到用戶的手機號上,你可以按照以下步驟進行 1.安裝 RabbitMQ 客戶端庫 首先,確保你已經安裝了 RabbitMQ 客戶端庫。你可以通過 NuGet 包管理器來安裝: dotnet add package RabbitMQ.Clien ...
  • 1.下載 Protocol Buffers 編譯器(protoc) 前往 Protocol Buffers GitHub Releases 頁面。在 "Assets" 下找到適合您系統的壓縮文件,通常為 protoc-{version}-win32.zip 或 protoc-{version}-wi ...
  • 簡介 在現代微服務架構中,服務發現(Service Discovery)是一項關鍵功能。它允許微服務動態地找到彼此,而無需依賴硬編碼的地址。以前如果你搜 .NET Service Discovery,大概率會搜到一大堆 Eureka,Consul 等的文章。現在微軟為我們帶來了一個官方的包:Micr ...
  • ZY樹洞 前言 ZY樹洞是一個基於.NET Core開發的簡單的評論系統,主要用於大家分享自己心中的感悟、經驗、心得、想法等。 好了,不賣關子了,這個項目其實是上班無聊的時候寫的,為什麼要寫這個項目呢?因為我單純的想吐槽一下工作中的不滿而已。 項目介紹 項目很簡單,主要功能就是提供一個簡單的評論系統 ...