自拍偷在线精品自拍偷,亚洲欧美中文日韩v在线观看不卡

新一代包管理工具 Pnpm

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

背景

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

npm 發(fā)展

npm2

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

從上圖中,我們可以看到

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

其中 2 和 3 這兩點(diǎn)的負(fù)面影響會隨著項目復(fù)雜度而上升,可能會導(dǎo)致的幾個問題:

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

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 依賴包路徑過長,超出操作系統(tǒng)最長路徑限制( windows:260 字符,macos:1024 字符),參見:

太深的層級導(dǎo)致文件查找復(fù)雜度上升,嚴(yán)重影響性能,增加耗時。

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

npm3

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

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

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

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

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

phantom_deps(幻影依賴)[7]。npm3不會以確定的方式安裝依賴項。舉例來說:我們在 NodeJS 中 require() 的函數(shù),不需要考慮配置文件 package.json 中是否有該依賴項。這可能會導(dǎo)致依賴版本不兼容,并且開發(fā)者不容易發(fā)現(xiàn);另外,由于`Nodejs`的依賴解析規(guī)則[8],這還會導(dǎo)致幻影 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 分身是指同一個依賴的不同版本會出現(xiàn)在 node_modules 中,比如項目中同時依賴了 A@1.0.0 和 A@2.0.0,無論是扁平化處理A@1.0.0 或 A@2.0.0,另一個依賴還是會被重復(fù),如果這樣的分身較多,就會導(dǎo)致一些潛在問題,比如擴(kuò)展包大小變大、相關(guān)類型校驗交叉等。

npm5

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

pnpm 簡介

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

pnpm 指 performant npm(高性能的 npm),如 pnpm 官網(wǎng)[10]所言,它是快速的,節(jié)省磁盤空間的包管理工具,同時,它也較好地支持了 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

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

pnpm 的原理

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

基于硬鏈接的 node_modules

pnpm 創(chuàng)建從全局存儲到項目中 node_modules 文件夾的硬鏈接[12],而硬鏈接指向磁盤上原始文件所在的同一位置,具體來說就是 node_modules 中每個包的每個文件都是來自內(nèi)容可尋址存儲[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 下面是一個 文件夾,而在其下面 的文件夾是一個基于內(nèi)容可尋址存儲的硬鏈接。同時,我們也可以通過 pnpm root 命令來打印當(dāng)前項目中存放模塊(modules)的有效目錄。

基于依賴解析的軟鏈接 symlinks

觀察以下依賴包結(jié)構(gòu):

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 內(nèi)引用了 bar 的軟鏈接 ../../bar@1.0.0/node_modules/bar,而在項目里引用 foo 的軟鏈接 ./.pnpm/foo@1.0.0/node_modules/foo,如果項目內(nèi)新增一個依賴包 qar@2.0.0,則其引用結(jié)構(gòu)如下:

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

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

通過基于硬鏈接的node_modules和基于依賴解析的軟鏈接原理,我們了解到,當(dāng)我們在相同操作系統(tǒng)下第二次安裝同一個依賴包時,我們需要做的僅僅是創(chuàng)建一個該依賴包對應(yīng)的硬鏈接,對于同一個依賴包的不同版本,也只有不同的部分會被重新保存起來,而具體有沒有 pnpm 是在哪里判斷的呢?全局的 pnpm 索引文件在 ~/.pnpm-store/v3/files?;诖耍褂糜叉溄幼屢蕾嚢陌惭b速度非???,同時也去除了冗余,節(jié)省較大磁盤空間。

symlinks 符號連接[16]

pnpm 使用

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

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 除了在開發(fā)體驗方面的優(yōu)越表現(xiàn),在項目集成方面也毫不遜色,對于較大型項目從 npm 或 yarn到pnpm遷移過程后,也得到了極大的優(yōu)化,結(jié)果如下:


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

通過以上數(shù)據(jù),我們可以 pnpm 在 CI 應(yīng)用中的良好表現(xiàn)。

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

pnpm 前置

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

{
"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 這些開源項目也轉(zhuǎn)而使用了它。使用pnpm run 結(jié)合 --filter 、 --recursive 和 --parallel 選項,可以指定特定包,并高速執(zhí)行相關(guān)命令。這樣做的好處是之前要另外安裝 lerna 這種 monorepo 管理工具的場景,現(xiàn)在 pnpm 可以包攬了。詳細(xì)文章可以參考這里 pnpm vs Lerna: filtering in a multi-package repository[22]。

總結(jié)

本文從 pnpm 的出現(xiàn)背景開始,簡要介紹了 npm 的發(fā)展過程及存在的問題,繼而對 pnpm 及其效果進(jìn)行了簡介,重點(diǎn)講述了 pnpm 的實現(xiàn)原理,并從應(yīng)用側(cè)選擇了四個點(diǎn)展開。

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

當(dāng)然,pnpm 使用過程中也存在一些問題,包括 Vue 官方在遷移過程中,也處理過部分問題。另外,一些包也存在兼容性問題,由于包自己實現(xiàn)了模塊解析,并沒有遵循相關(guān)規(guī)范。但 pnpm 也提供了相關(guān)解決方法。具體參考 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的依賴解析規(guī)則: 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 官網(wǎng): 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]內(nèi)容可尋址存儲: 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

責(zé)任編輯:武曉燕 來源: 青梅主碼
相關(guān)推薦

2022-02-07 23:03:07

Python工具管理庫

2022-08-03 08:02:46

PDM工具Python

2022-06-28 16:30:26

管理工具Python

2022-07-01 09:17:14

Pythonpoetry工具

2022-05-16 09:14:28

前端構(gòu)建工具

2022-05-23 08:59:02

piniavue插件

2025-04-17 03:00:00

dbt數(shù)據(jù)轉(zhuǎn)換工具開源

2022-03-10 16:01:29

Playwright開源

2012-07-02 10:36:19

菲亞特

2020-10-09 11:50:10

ReactRecoil前端

2020-11-13 15:40:18

React前端Recoil

2013-01-04 16:15:08

微軟ERPDynamics AX

2023-12-30 16:30:29

開發(fā)者工具Vite

2016-01-26 11:58:12

2013-10-21 10:01:04

編碼工具擴(kuò)展

2010-05-05 18:05:00

新一代數(shù)據(jù)中心

2009-09-02 16:10:40

ADSL技術(shù)

2009-03-11 13:02:20

存儲虛擬化數(shù)據(jù)中心

2010-06-17 16:54:49

新一代Hotmail

2011-03-31 17:49:51

微軟嵌入式WindowsEmbe
點(diǎn)贊
收藏

51CTO技術(shù)棧公眾號