成人免费xxxxx在线视频软件_久久精品久久久_亚洲国产精品久久久_天天色天天色_亚洲人成一区_欧美一级欧美三级在线观看

新一代包管理工具 Pnpm

開發 開發工具
在 npm2 發展階段,安裝依賴是相對比較直接的,它會直接按照配置文件 package.json 中的依賴項去下載相關依賴包,而依賴包的組織形式則是按照樹形結構去排列的。

背景

如果你從事過前端方面的工作和開發,相信你對 npm 和 yarn 這樣的工具已經再熟悉不過了。作為包管理工具,npm 已經有了長足的歷史[1]。一個項目初始化過程都需要通過 npm install 命令安裝相關的依賴到 node_modules 目錄下,對于較大型的前端項目,node_modules 的大小很多時候是超乎我們想象的。而 npm 在版本更新迭代的過程中,也一直在優化這個問題,下面我們就簡要聊聊 npm 的發展歷程。

npm 發展

npm2

在 npm2 發展階段,安裝依賴是相對比較直接的,它會直接按照配置文件 package.json 中的依賴項去下載相關依賴包,而依賴包的組織形式則是按照樹形結構去排列的。由于不同的包的依賴關系在版本上差異較大,依賴關系相對復雜,所以 npm2 直接按照配置去下載并組織依賴的方式,是簡單明晰的做法,保證了各個依賴的獨立性,在依賴變更時,相互并不影響,其關系可以通過下圖來描述:

從上圖中,我們可以看到

  • A、B、C 包相互獨立
  • A、B、C 包可能會依賴相同的包,比如 D@1.0
  • A、B、C 包可能會存在較深的依賴層級,比如 C package

其中 2 和 3 這兩點的負面影響會隨著項目復雜度而上升,可能會導致的幾個問題:

  • 較大的冗余。多次下載的相同的依賴包 D@1.0,無法實現共享
  • 較深層級的依賴樹。

Too many dependencies break the Windows file system[2]

Maximum Path Length Limitation[3]

Why does the 260 character path length limit exist in Windows?[4]

node_modules 依賴包路徑過長,超出操作系統最長路徑限制( windows:260 字符,macos:1024 字符),參見:

太深的層級導致文件查找復雜度上升,嚴重影響性能,增加耗時

Note: 通過 npm ls --depth=n 查看項目相關依賴層級深度

npm3

為解決 npm2 中存在的冗余和依賴樹問題,npm3 對依賴項進行了依賴扁平化討論和處理[5]

扁平化具體來講就是依賴不在按照樹型進行安裝,而是安裝將依賴安裝在同級目錄下,npm install安裝依賴時,會按照配置文件 package.json 里的依賴順序進行解析,遇到新包就把它放在第一層級的目錄(如 D@1.0、E@1.0、F@1.0),后面如果遇到第一級目錄已有的包,會先進行依賴版本判斷,如果版本一樣則忽略,否則會按照 npm2 的方式依次掛在依賴包目錄下,這樣處理的原理遵循了`Nodejs`的依賴解析規則[6]:當前目錄下沒有找到node_modules,它將遞歸解析父目錄下的node_modules。

使用 npm3 安裝依賴后如下圖:

這種扁平化處理方式一定程度上緩解了冗余和依賴樹問題,同時 npm3 還支持動態安裝更新包,如果依賴有更新,可以通過 npm dedupe 命令對依賴樹進行優化。

但是 npm3 也存在部分問題,比如:

phantom_deps(幻影依賴)[7]。npm3不會以確定的方式安裝依賴項。舉例來說:我們在 NodeJS 中 require() 的函數,不需要考慮配置文件 package.json 中是否有該依賴項。這可能會導致依賴版本不兼容,并且開發者不容易發現;另外,由于`Nodejs`的依賴解析規則[8],這還會導致幻影 node_modules ,即依賴向上查找,可能會越過代碼目錄自身的 node_modules 。如下:

- my-monorepo/
- package.json
- node_modules/
- semver/
- ...
- my-monorepo/my-library/
- package.json
- lib/
- index.js
- node_modules/
- brace-expansion
- minimatch
- ...

my-monorepo/my-library/lib/index.js 可能使用的是my-monorepo/node_modules 中的依賴,而非自身目錄 my-monorepo/my-library/node_modules

npm doppelgangers(npm 分身)[9]。簡單來講,npm 分身是指同一個依賴的不同版本會出現在 node_modules 中,比如項目中同時依賴了 A@1.0.0 和 A@2.0.0,無論是扁平化處理A@1.0.0 或 A@2.0.0,另一個依賴還是會被重復,如果這樣的分身較多,就會導致一些潛在問題,比如擴展包大小變大、相關類型校驗交叉等

npm5

npm5 通過添加 lock 文件來記錄依賴樹信息,進行依賴鎖定,從而唯一確定 node_modules 的結構,這樣處理可以保證團隊成員使用同一份node_modules依賴結構。但是,我們前文提到的平鋪式的算法的復雜性、幻影依賴和分身問題仍然沒有解決。

pnpm 簡介

前文我們大致梳理了 npm 的發展和遺留問題。而 pnpm 比較巧妙地解決了它們,并且極大地提升了依賴包管理的效率。

pnpm 指 performant npm(高性能的 npm),如 pnpm 官網[10]所言,它是快速的,節省磁盤空間的包管理工具,同時,它也較好地支持了 workspace 和 monorepos。

pnpm 效果

與 npm、yarn、yarn pnp 工具鏈效果對比,來自 pnpm benchmarks[11]

action

cache

lockfile

node_modules

npm

pnpm

Yarn

Yarn PnP

install




1m 9.5s

15.3s

16.6s

23.6s

install

?

?

?

2.4s

1.3s

2.3s

n/a

install

?

?


14.8s

4s

6.8s

1.5s

install

?



21.8s

8.9s

11.2s

6.2s

install


?


35.4s

13.4s

12s

17.9s

install

?


?

3.1s

1.9s

7s

n/a

install


?

?

2.4s

1.3s

7.6s

n/a

install



?

3s

6.1s

11.8s

n/a

update

n/a

n/a

n/a

2.3s

11.8s

15.5s

28.3s

從上表數據我們可以看出,pnpm 的各項性能均比其它包管理工具有優勢,那你可能會想,為什么 pnpm 有如此優越的表現,接下來我們聊聊 pnpm 的主要原理

pnpm 的原理

pnpm 主要有兩個不同與其包管理工具的特性:

基于硬鏈接的 node_modules

pnpm 創建從全局存儲到項目中 node_modules 文件夾的硬鏈接[12],而硬鏈接指向磁盤上原始文件所在的同一位置,具體來說就是 node_modules 中每個包的每個文件都是來自內容可尋址存儲[13]的硬鏈接,簡言之,就是特定版本和名稱的包全局只有一份。舉例來看:

node_modules
└── .pnpm
├── bar@1.0.0
│ └── node_modules
│ └── bar -> <store>/bar
│ ├── index.js
│ └── package.json
└── foo@1.0.0
└── node_modules
└── foo -> <store>/foo
├── index.js
└── package.json

node_modules 下面的唯一文件夾叫做 .pnpm, .pnpm 下面是一個 文件夾,而在其下面 的文件夾是一個基于內容可尋址存儲的硬鏈接。同時,我們也可以通過 pnpm root 命令來打印當前項目中存放模塊(modules)的有效目錄。

基于依賴解析的軟鏈接 symlinks

觀察以下依賴包結構:

node_modules
├── foo -> ./.pnpm/foo@1.0.0/node_modules/foo
└── .pnpm
├── bar@1.0.0
│ └── node_modules
│ └── bar -> <store>/bar
└── foo@1.0.0
└── node_modules
├── foo -> <store>/foo
└── bar -> ../../bar@1.0.0/node_modules/bar

我們可以看到在 foo@1.0.0/node_modules/bar 內引用了 bar 的軟鏈接 ../../bar@1.0.0/node_modules/bar,而在項目里引用 foo 的軟鏈接 ./.pnpm/foo@1.0.0/node_modules/foo,如果項目內新增一個依賴包 qar@2.0.0,則其引用結構如下:

node_modules
├── foo -> ./.pnpm/foo@1.0.0/node_modules/foo
└── .pnpm
├── bar@1.0.0
│ └── node_modules
│ ├── bar -> <store>/bar
│ └── qar -> ../../qar@2.0.0/node_modules/qar
├── foo@1.0.0
│ └── node_modules
│ ├── foo -> <store>/foo
│ ├── bar -> ../../bar@1.0.0/node_modules/bar
│ └── qar -> ../../qar@2.0.0/node_modules/qar
└── qar@2.0.0
└── node_modules
└── qar -> <store>/qar

根據前文我們介紹到的`Nodejs`的依賴解析規則[14],foo@1.0.0/node_modules/foo/index.js 中所需的依賴包 bar,實際上使用的是bar@1.0.0/node_modules/bar中的內容,因此,只有真正在依賴項中的包才能被訪問到。而對于不同的 peer dependencies 的依賴解析原理,可以參考這里 How peers are resolved[15]

通過基于硬鏈接的node_modules和基于依賴解析的軟鏈接原理,我們了解到,當我們在相同操作系統下第二次安裝同一個依賴包時,我們需要做的僅僅是創建一個該依賴包對應的硬鏈接,對于同一個依賴包的不同版本,也只有不同的部分會被重新保存起來,而具體有沒有 pnpm 是在哪里判斷的呢?全局的 pnpm 索引文件在 ~/.pnpm-store/v3/files。基于此,使用硬鏈接讓依賴包的安裝速度非常快,同時也去除了冗余,節省較大磁盤空間。

symlinks 符號連接[16]

pnpm 使用

pnpm 的具體使用這里我們不展開介紹了,可以查看官網使用方法[17]和CLI 命令[18]即可。這里只提幾個有意思的點

CI 集成

在 GitHub Actions 上,你可以像這樣使用 pnpm 安裝和緩存依賴項,配置文件目錄: .github/workflows/NAME.yml。

name: pnpm Example Workflow
on:
push:
jobs:
build:
runs-on: ubuntu-20.04
strategy:
matrix:
node-version: [15]
steps:
- uses: actions/checkout@v2
- uses: pnpm/action-setup@v2.0.1
with:
version: 6.20.3
- name: Use Node.js ${{ matrix.node-version }}
uses: actions/setup-node@v2
with:
node-version: ${{ matrix.node-version }}
cache: 'pnpm'
- name: Install dependencies
run: pnpm install

pnpm 除了在開發體驗方面的優越表現,在項目集成方面也毫不遜色,對于較大型項目從 npm 或 yarn到pnpm遷移過程后,也得到了極大的優化,結果如下:


Without cache

With cache

yarn 2 (without dedupe)

6min 31s

1min 11s

yarn 3 (without dedupe)

4min 50s

57s

yarn 3

4min 1s

50s

yarn 3 (optimized)

1min 10

45s

pnpm

58s

24s

通過以上數據,我們可以 pnpm 在 CI 應用中的良好表現。

具體可以參考這篇最佳實踐 A story of how we migrated to pnpm[19]

pnpm 前置

項目中使用 pnpm 時,如果你不希望項目內其他人使用 npm i 或 yarn這類包管理器,可以在 package.json 配置文件中添加預安裝 preinstall 配置項,從而規范使用統一的包管理器。

{
"scripts": {
"preinstall": "npx only-allow pnpm"
}
}

管理 NodeJS 版本

在以前,如果你同時支撐了多個項目,而且需要在其中切換,你可能需要切換不同的 NodeJS 版本,也許你會用到像 nvm 或 Volta[20] 這樣的 NodeJS 版本管理器,而 pnpm 從 v6.12.0 版本后支持了 pnpm env[21] 命令,你可以使用它來安裝并指定使用哪個版本的 NodeJS ,是不是方便了很多。

monorepo 支持

因為pnpm 對 monorepos 的大力支持,像 Vue、Vite 這些開源項目也轉而使用了它。使用pnpm run 結合 --filter 、 --recursive 和 --parallel 選項,可以指定特定包,并高速執行相關命令。這樣做的好處是之前要另外安裝 lerna 這種 monorepo 管理工具的場景,現在 pnpm 可以包攬了。詳細文章可以參考這里 pnpm vs Lerna: filtering in a multi-package repository[22]。

總結

本文從 pnpm 的出現背景開始,簡要介紹了 npm 的發展過程及存在的問題,繼而對 pnpm 及其效果進行了簡介,重點講述了 pnpm 的實現原理,并從應用側選擇了四個點展開。

pnpm 作為新一代包管理器,自身有不少優越的表現,它通過硬鏈接和軟鏈接的方式,解決了 npm幻影依賴和分身問題,并且較好地解決了依賴包復用問題,從而實現了依賴包高效快速的安裝。需要特別注意的是 pnpm 嚴格遵循了 Nodejs 依賴解析規則,規避了之前任意依賴包的訪問修改問題。

當然,pnpm 使用過程中也存在一些問題,包括 Vue 官方在遷移過程中,也處理過部分問題。另外,一些包也存在兼容性問題,由于包自己實現了模塊解析,并沒有遵循相關規范。但 pnpm 也提供了相關解決方法。具體參考 pnpm FAQ[23]。

參考資料:

[1]歷史: https://github.com/npm/cli/blob/latest/changelogs/CHANGELOG-1.md

[2]Too many dependencies break the Windows file system: https://github.com/npm/npm/issues/3697

[3]Maximum Path Length Limitation: https://docs.microsoft.com/en-us/windows/win32/fileio/maximum-file-path-limitation?tabs=cmd

[4]Why does the 260 character path length limit exist in Windows?: https://stackoverflow.com/questions/1880321/why-does-the-260-character-path-length-limit-exist-in-windows

[5]依賴扁平化討論和處理: https://github.com/npm/cli/blob/latest/changelogs/CHANGELOG-3.md

[6]Nodejs的依賴解析規則: https://nodejs.org/api/modules.html#all-together

[7]phantom_deps(幻影依賴): https://rushjs.io/pages/advanced/phantom_deps/

[8]npm doppelgangers(npm 分身): https://rushjs.io/pages/advanced/npm_doppelgangers/

[9]pnpm 官網: https://pnpm.io/

[10]pnpm benchmarks: https://pnpm.io/zh/benchmarks

[11]硬鏈接: https://zh.wikipedia.org/wiki/%E7%A1%AC%E9%93%BE%E6%8E%A5

[12]內容可尋址存儲: https://en.wikipedia.org/wiki/Content-addressable_storage

[13]How peers are resolved: https://pnpm.io/zh/how-peers-are-resolved

[14]symlinks 符號連接: https://zh.wikipedia.org/wiki/%E7%AC%A6%E5%8F%B7%E9%93%BE%E6%8E%A5

[15]使用方法: https://pnpm.io/zh/pnpm-cli

[16]CLI 命令: https://pnpm.io/zh/cli/add

[17]A story of how we migrated to pnpm: https://divriots.com/blog/switching-to-pnpm

[18]Volta: https://volta.sh/

[19]pnpm env: https://pnpm.io/zh/cli/env

[20]pnpm vs Lerna: filtering in a multi-package repository: https://medium.com/pnpm/pnpm-vs-lerna-filtering-in-a-multi-package-repository-1f68bc644d6a

[23]pnpm FAQ: https://pnpm.io/faq#pnpm-does-not-work-with-your-project-here

責任編輯:武曉燕 來源: 青梅主碼
相關推薦

2022-02-07 23:03:07

Python工具管理庫

2022-06-28 16:30:26

管理工具Python

2022-07-01 09:17:14

Pythonpoetry工具

2022-08-03 08:02:46

PDM工具Python

2022-05-16 09:14:28

前端構建工具

2022-05-23 08:59:02

piniavue插件

2025-04-17 03:00:00

dbt數據轉換工具開源

2020-10-09 11:50:10

ReactRecoil前端

2020-11-13 15:40:18

React前端Recoil

2013-01-04 16:15:08

微軟ERPDynamics AX

2012-07-02 10:36:19

菲亞特

2022-03-10 16:01:29

Playwright開源

2023-12-30 16:30:29

開發者工具Vite

2013-10-21 10:01:04

編碼工具擴展

2016-01-26 11:58:12

2010-05-05 18:05:00

新一代數據中心

2025-02-13 09:37:58

2016-12-11 10:35:52

2010-02-07 15:50:33

Android手機

2013-09-24 10:38:23

點贊
收藏

51CTO技術棧公眾號

主站蜘蛛池模板: 亚洲精品在线视频 | 欧美性生活视频 | 成人av片在线观看 | 国产精品自产拍 | 久久国产精品一区二区三区 | 亚洲免费视频在线观看 | 国产aaaaav久久久一区二区 | 免费观看成人鲁鲁鲁鲁鲁视频 | 国产成人jvid在线播放 | 日本在线一二 | 成人在线视频免费观看 | 2021狠狠干 | 欧美一区二区三区在线 | 国产精品久久久久久久久久久久久久 | 综合久久亚洲 | 亚洲三区视频 | 毛片1 | 免费超碰 | 黄色毛片网站在线观看 | 中文字幕在线一区 | 久草网站 | 一级毛片中国 | 久久精品国产亚洲夜色av网站 | 亚洲天堂中文字幕 | 国产在线精品一区二区 | 99精品99久久久久久宅男 | 亚洲精品视频在线看 | 久久美女视频 | 国产欧美一区二区三区日本久久久 | 超碰欧美 | 久久国产欧美日韩精品 | 91精品国产色综合久久 | 岛国av免费观看 | 欧美成人一区二免费视频软件 | 久久精品久久久久久 | 九九综合 | 亚洲网在线 | 日韩视频在线一区二区 | 99re免费| 国产人久久人人人人爽 | 亚洲免费高清 |