1、pnpm是什么?
現(xiàn)代的包管理工具 pnpm( performant npm ),意思是高性能的 npm
它由 npm/yarn 衍生而來,但卻解決了 npm/yarn 內部潛在的 bug,并且極大了地優(yōu)化了性能
2、特性概覽
(1)速度快
官方的benchmark 數(shù)據(jù)是這樣的,但是實際使用發(fā)現(xiàn)pnpm的第一次下載包速度跟yarn是差不多的,其優(yōu)勢體現(xiàn)在第二次下載相同的包更快
(2)高效利用磁盤空間
pnpm 內部使用基于內容尋址的文件系統(tǒng)來存儲磁盤上所有的文件,這個文件系統(tǒng)出色的地方在于
1、不會重復安裝同一個包。用 npm/yarn 的時候,如果 100 個項目都依賴 lodash,那么 lodash 很可能就被安裝了 100 次,磁盤中就有 100 個地方寫入了這部分代碼。但在使用 pnpm 只會安裝一次,磁盤中只有一個地方寫入,后面再次使用都會直接使用 hardlink(硬鏈接)
2、即使一個包的不同版本,pnpm 也會極大程度地復用之前版本的代碼。舉個例子,比如 lodash 有 100 個文件,更新版本之后多了一個文件,那么磁盤當中并不會重新寫入 101 個文件,而是保留原來的 100 個文件的 hardlink,僅僅寫入那一個新增的文件。
(3)*支持monorepo
隨著前端工程的日益復雜,越來越多的項目開始使用 monorepo。之前對于多個項目的管理,我們一般都是使用多個 git 倉庫,但 monorepo 的宗旨就是用一個 git 倉庫來管理多個子項目,所有的子項目都存放在根目錄的packages目錄下,那么一個子項目就代表一個package。
3、pnpm依賴原理
1、npm、yarn安裝包的問題
在 pnpm 出現(xiàn)以前,npm 和 yarn 為了提高包的復用率,都采用了扁平化的裝包策略。扁平化的安裝方式會導致我們的 node_modules 文件夾和 package.json 存在很大的出入,比如你install一個包 express,但是你的node_modules下會有很多包
這個時候會有一些問題
(1)幽靈依賴
從目前的包引用方式來說,inport的時候我們會從node_modules的文件夾中尋找,按照上面的圖中所示,如果我們在package.json中沒有accepts,其實我們也是可以引用到的,因為她確實存在,這時候我們訪問的就是未申明npm包,如果某一天express主包不再依賴accepts,這個時候項目就會有依賴缺失的問題。 我們把這種主包依賴的子包,未被申明而在項目中使用,可以理解成是主包夾帶的包,我們稱之為 幽靈依賴。
(2)包版本的不確定性
這個很好理解,如果A、B兩個主包都依賴accepts包,但是A依賴accepts@1.0,B依賴accepts@2.0 ,那node_modules下的扁平結構是展示1.0 還是 2.0 呢?目前的方式是誰后安裝的誰就顯示。 這種不確定性在開發(fā)中引起的問題也不在少數(shù) 「別人用這個包可以解決這個問題,但是我安裝這個包就不能解決」,往往就是這個原因導致的。
(3)依賴重復安裝
這個也很好理解,AB都依賴accepts,依賴不同的版本,無論node_modules的頂層提升了哪個版本,這個包都是會被安裝兩次的。
2、pnpm的安裝包方式
同樣的,使用pnpm安裝一個express,安裝結構如下
可以看到 node_modules 結構非常清晰,但是這個 express 文件夾只是一個軟鏈接, 它的真正存儲的地方在圖中的 .pnpm 文件夾中
我們看一下pnpm官方對這一現(xiàn)象的圖示說明:
頂級外層來看,格式很清晰,.pnpm中也是嵌套的。這是因為pnpm的node_modules布局使用的是符號鏈接來創(chuàng)建依賴關系的嵌套結構。.pnpm內部的每個包中的每個文件都是只用硬鏈接指向了.pnpm store 中的文件
這樣的好處就是會讓我們的node_modules很清晰,內部的包可以和package.json中的依賴對應起來,一目了然,我們安裝什么里面就有什么
這樣幽靈依賴的問題就解決了,包版本不確定性的問題也就解決了。畢竟頂層就只有我們手動安裝的包,其他依賴包都收在.pnpm中。這樣無論是哪個版本都會平鋪在這里供你使用。 這個平鋪的方式就是通過鏈接的形式進行引用
問題是上面的軟鏈接、硬鏈接是啥呢?簡單說明一下
軟鏈接:類似windows系統(tǒng)的快捷方式;軟鏈接里面存放的是源文件的路徑,指向源文件;
硬鏈接:是計算機文件系統(tǒng)中的多個文件平等地共享同一個文件存儲單元(如MFT條目、inode),可以實現(xiàn)多對一的關系,pnpm主要利用的是這一特性,這就很容易說明多個項目用到同一個包,就不用再重復下載包了,只需要管理好.pnpm
store中的對應源文件就可以了
4、日常使用
pnpm 使用命令和之前 npm/yarn 差不多,甚至可以無縫遷移到 pnpm 上來,常用命令主要是
1、pnpm install :安裝依賴
2、pnpm update :更新依賴,根據(jù)指定的范圍將包更新到最新版本,monorepo 項目中可以通過 --filter 來指定更新某個項目的某個包
3、pnpm uninstall :刪除依賴,根據(jù)指定的范圍將包刪除,monorepo 項目中可以通過 --filter 來指定刪除某個項目的某個包
4、pnpm add:添加包
5、pnpm filter?
5、pnpm Demo演示
上述提到的filter到底是什么功用呢?結合以下項目具體闡述
1、先配置最外層主目錄結構
pnpm-workspace.yaml 配置
1) pnpm-workspace.yaml 配置
packages:
- "packages/**"
意思是定義pnpm的workspace空間,項目的多包文件入口是packages
2、在packages中創(chuàng)建多個項目,各個項目是獨立的,你可以創(chuàng)建vue、react項目、方法庫等,各項目是獨立的,但是依賴包可以進行共享
我現(xiàn)在的目錄結構如下:
PNPMDEMO
├── package.json
├── packages
│ ├── components
│ │ ├── index.js
│ │ └── package.json
│ ├── reactApp
│ │ ├── public
│ │ └── src
│ │ └── .....
│ │ └── package.json
│ │ └── vite.config.js
│ ├── utils
│ │ ├── index.js
│ │ └── package.json
├── pnpm-lock.yaml
└── pnpm-workspace.yaml
3、編寫每個項目的package.json,其實主要是編寫一下名稱,方便以后使用
(我在utils/packages.json中安裝了dayjs,在utils/packages.json安裝了lodash)
{
"name": "@packages/utils",
"version": "2.0.0",
"description": "",
"main": "index.js",
"scripts": {
"test": "echo "Error: no test specified" && exit 1"
},
"keywords": [],
"author": "",
"type": "module",
"license": "ISC",
"dependencies": {
"dayjs": "^1.11.5"
}
}
4、安裝依賴
1)在根目錄下安裝依賴的話,這個依賴可以在所有的packages中使用
2)package下各項目安裝依賴,問題來了? 我們需要cd到package的所在目錄嘛?答案是:不需要的,可以通過pnpm強大的filter命令執(zhí)行操作
5、命令
在根目錄可以進行總目錄的命令操作,也可對packages下的各項目進行整體命令操作pnpm -w <package_selector>
pnpm --filter <package_selector>
1)全局安裝,下面各項目直接可以調用總包 :pnpm -w add shortid
2)對packages下的各項目執(zhí)行打包
"scripts": {
"test": "echo "Error: no test specified" && exit 1",
"build": "pnpm --filter * build"
},
3)對packages下某個項目進行增加依賴操作
例如:在@packages/components中安裝lodash,在@packages/utils中安裝一個dayjs
pnpm --filter @packages/components add lodash
pnpm --filter @packages/utils add dayjs
6、實例場景
在utils項目內定義一個方法,在reactAPP進行引用
1、在utils項目內定義一個方法
import dayjs from "dayjs";
export const formatDay = () => {
return dayjs().format('YYYY-MM-DD')
}
2、 在根目錄下執(zhí)行`pnpm -F @packages/react-app add @packages/utils@*`,
表示@packages/components安裝@packages/utils,其中的@*表示默認同步最新版本,省去每次都要同步最新版本的問題
完成后在reactAPP的packages.json中是這樣的
{
"name": "@packages/react-app",
"private": true,
"version": "0.0.0",
"type": "module",
"scripts": {
"dev": "vite",
"build": "vite build",
"preview": "vite preview"
},
"dependencies": {
"@packages/utils": "workspace:*", //引入的utils的包
"react": "^18.2.0",
"react-dom": "^18.2.0"
},
"devDependencies": {
"@types/react": "^18.0.17",
"@types/react-dom": "^18.0.6",
"@vitejs/plugin-react": "^2.1.0",
"vite": "^3.1.0"
}
}
3、 在reactApp下的調用utils定義的方法文章來源:http://www.zghlxwxcb.cn/news/detail-416665.html
import { useState } from 'react'
import {formatDay,formatMth} from '@packages/utils'
import './App.css'
function App() {
console.log(formatMth());
return (
<div className="App">
{formatDay()}
</div>
)
}
export default App
7、包提升
就是實現(xiàn)在packages下的某個項目中引入一個第三方庫,將此第三方庫提升到目錄層,其他packages下的所有項目都可以使用此第三方庫
1)在components項目中引入lodash
2)配置.npmrc文件public-hoist-pattern[]=lodash
3)執(zhí)行pnpm install
,在reactApp下即可使用lodash的方法文章來源地址http://www.zghlxwxcb.cn/news/detail-416665.html
到了這里,關于pnpm學習的文章就介紹完了。如果您還想了解更多內容,請在右上角搜索TOY模板網以前的文章或繼續(xù)瀏覽下面的相關文章,希望大家以后多多支持TOY模板網!