npm 本身的深入用法(package.json、指令、版本、腳本)見 npm 完全指南;套件管理器在生態中的定位見 JavaScript 生態全景。
目錄
什麼是套件管理器?
套件管理器(Package Manager) 負責:從 npm registry 下載你專案依賴的套件、裝進 node_modules、用 lockfile 鎖定版本、並提供執行 package.json scripts 的入口。
package.json(宣告要哪些套件)
│ 套件管理器
▼
node_modules(實際裝進來的套件)+ lockfile(鎖定確切版本)
npm、yarn、pnpm 做的是同一件事,差別在怎麼存、怎麼連、多快、多省空間、多嚴格。
三者簡介
| 管理器 | 來歷 | 一句話特色 |
|---|---|---|
| npm | Node.js 內建(預設) | 最普及、零安裝即有 |
| yarn | Facebook 2016 推出 | 當年比舊 npm 快、帶 lockfile/workspaces;v2+(Berry)改用 PnP |
| pnpm | performant npm | 全域 store + 連結 → 最省磁碟、最快、最嚴格 |
補充:Bun 也內建套件管理(
bun install),相容 npm 生態、主打速度。本篇聚焦三大主流。
共同點
換管理器多數情況不必改程式,因為它們:
- 用同一個 npm registry:套件來源相同
- 都讀
package.json:依賴宣告格式一致 - 指令概念相近:install / add / remove / run
- 都產生 lockfile:只是檔名與格式不同
所以「換 pnpm」通常只是換指令與重裝,不是換生態。
關鍵差異
| 面向 | npm | yarn (classic) | pnpm |
|---|---|---|---|
| node_modules 結構 | 扁平、提升(hoisting) | 扁平、提升 | 符號連結 + 全域 store |
| 幽靈依賴 | 易發生 | 易發生 | 嚴格避免 |
| 磁碟用量 | 高(每專案複製) | 高 | 低(硬連結共用) |
| 安裝速度 | 中 | 快 | 最快 |
| lockfile | package-lock.json |
yarn.lock |
pnpm-lock.yaml |
| monorepo | workspaces | workspaces | workspaces(體驗佳) |
| 內建於 Node | ✅ | ❌ | ❌ |
下面挑最關鍵的幾點展開。
node_modules 結構與幽靈依賴
這是 pnpm 與 npm/yarn 最本質的差異。
npm / yarn:扁平提升(hoisting)
把所有依賴(含依賴的依賴)盡量「提升」到 node_modules 頂層攤平:
node_modules/
your-lib/
lodash/ ← 其實是 your-lib 的依賴,卻被提到頂層
問題:幽靈依賴(phantom dependencies)——你的程式可以 import lodash,即使你沒在 package.json 宣告它(它只是別人的依賴剛好被提上來)。哪天那個間接依賴改版移除 lodash,你的程式就突然壞掉。
pnpm:符號連結 + 全域 store
pnpm 的 node_modules 只放你真正宣告的套件(用 symlink 指向全域 store),間接依賴藏在 .pnpm/ 下:
node_modules/
your-lib → 連結到全域 store
.pnpm/ ← 間接依賴放這,不攤平到頂層
結果:你只能 import 你宣告過的套件,沒宣告的 import 不到 → 杜絕幽靈依賴,依賴關係更誠實。
磁碟與速度
pnpm 用「全域內容定址 store + 硬連結」:同一版套件在整台機器只存一份,各專案用硬連結指過去,不重複複製。
npm/yarn:每個專案各自複製一份 node_modules → 10 個專案 = 10 份
pnpm: 全域存一份,專案用硬連結 → 10 個專案幾乎不額外佔空間
- 磁碟:pnpm 大幅省(多專案時差距明顯)
- 速度:pnpm 安裝通常最快(已在 store 的套件直接連結,不必重抓重解壓)
Lockfile
三者都用 lockfile 鎖定確切版本與依賴樹,確保「在你機器和 CI 上裝到的完全一樣」:
| 管理器 | lockfile |
|---|---|
| npm | package-lock.json |
| yarn | yarn.lock |
| pnpm | pnpm-lock.yaml |
原則:lockfile 一定要進版控,且團隊統一一種管理器——混用會產生多個 lockfile、裝出不一致的依賴樹。
Monorepo / Workspaces
三者都支援 workspaces(一個 repo 管多個 package,彼此可本地互相引用):
// package.json(根)
{ "workspaces": ["packages/*"] }
- pnpm 在 monorepo 的依賴隔離與速度上體驗最佳,是大型 monorepo 的熱門選擇
- 也常搭配 Turborepo / Nx 等 monorepo 工具
指令對照
| 動作 | npm | yarn | pnpm |
|---|---|---|---|
| 安裝全部依賴 | npm install |
yarn |
pnpm install |
| 加套件 | npm install pkg |
yarn add pkg |
pnpm add pkg |
| 加開發依賴 | npm install -D pkg |
yarn add -D pkg |
pnpm add -D pkg |
| 移除 | npm uninstall pkg |
yarn remove pkg |
pnpm remove pkg |
| 執行 script | npm run dev |
yarn dev |
pnpm dev |
| 執行一次性套件 | npx pkg |
yarn dlx pkg |
pnpm dlx pkg |
| CI 嚴格安裝 | npm ci |
yarn install --frozen-lockfile |
pnpm install --frozen-lockfile |
怎麼選
要零安裝、最普及、團隊不想折騰
→ npm(Node 內建,預設選擇)
多專案 / monorepo、在意磁碟與速度、想杜絕幽靈依賴
→ pnpm(現代專案越來越主流)
既有專案already用 yarn、或需要 Yarn Berry 的 PnP / 特定功能
→ yarn
實務建議:
- 新專案:npm(簡單)或 pnpm(效能 + 嚴格),二選一
- monorepo / 多專案開發:pnpm 體驗最佳
- 最重要的是團隊統一一種,並把 lockfile 進版控
常見問題
問題 1:換套件管理器要改程式嗎?
通常不用。它們用同一個 registry、讀同一份 package.json,換的只是指令與 node_modules 的組織方式。換時刪掉舊 node_modules 與舊 lockfile,用新管理器重裝即可。
問題 2:什麼是幽靈依賴(phantom dependency)?
你能 import 一個沒在 package.json 宣告的套件——因為它是別人的間接依賴被「提升」到 node_modules 頂層。風險是該間接依賴一改版移除它,你的程式就壞。pnpm 用嚴格的 node_modules 結構杜絕這問題。
問題 3:pnpm 為什麼比較省空間又快?
它把每版套件在整台機器只存一份(全域 content-addressable store),各專案用硬連結指過去,不重複複製、不重複下載解壓。多專案時省磁碟、安裝也快。
問題 4:可以同時用 npm 和 pnpm 嗎?
同一個專案不建議——會產生多個 lockfile、依賴樹不一致。團隊應統一一種,並只提交那一種 lockfile。
問題 5:lockfile 要進版控嗎?
要。lockfile 鎖定確切版本與依賴樹,確保所有人與 CI 裝到完全一致的依賴。不提交會導致「我的機器正常、別人/CI 壞掉」。
總結
核心要點
- npm / yarn / pnpm 都是 JS 套件管理器,用同一 registry、讀同一 package.json,做同一件事
- 差別在怎麼存與連:npm/yarn 扁平提升(易有幽靈依賴);pnpm 用全域 store + 連結(省磁碟、快、杜絕幽靈依賴)
- 三者都有 lockfile(檔名不同)與 workspaces(monorepo)
- 選型:npm(內建普及)/ pnpm(效能 + 嚴格 + monorepo)/ yarn(既有專案 / Berry PnP)
- 鐵則:團隊統一一種 + lockfile 進版控
快速參考
| npm | yarn | pnpm | |
|---|---|---|---|
| 內建 Node | ✅ | ❌ | ❌ |
| 磁碟 | 高 | 高 | 低 |
| 速度 | 中 | 快 | 最快 |
| 幽靈依賴 | 易 | 易 | 杜絕 |
| lockfile | package-lock.json | yarn.lock | pnpm-lock.yaml |
建立日期:2026-06-18