pnpm (performant npm) เป็น Package Manager สำหรับ Node.js ที่เร็วกว่าและประหยัด Disk กว่า npm อย่างมาก ด้วยเทคนิค Content-Addressable Storage ที่เก็บ Package เพียงสำเนาเดียวแล้วใช้ Symlink แทนการ Copy ทำให้โปรเจกต์ 10 โปรเจกต์ที่ใช้ React ก็เก็บ React แค่ชุดเดียว
ในปี 2026 pnpm กลายเป็นทางเลือกหลักของทีม Dev ขนาดใหญ่ โดยเฉพาะ Monorepo และ CI/CD Pipeline ที่ต้องการความเร็วและประหยัดพื้นที่
pnpm vs npm vs yarn vs bun — เปรียบเทียบ
| Feature | npm | yarn (v4) | pnpm | bun |
|---|---|---|---|---|
| Disk Usage | สูงมาก (copy ทุก project) | สูง (PnP ลดได้) | ต่ำมาก (content-addressable) | ต่ำ |
| Install Speed | ช้าที่สุด | ปานกลาง | เร็วมาก | เร็วที่สุด |
| Monorepo | workspaces (basic) | workspaces (ดี) | workspaces (ดีมาก) | workspaces (basic) |
| Strict node_modules | ไม่ (hoisting) | PnP mode | Yes (default) | ไม่ |
| Lockfile | package-lock.json | yarn.lock | pnpm-lock.yaml | bun.lockb |
| Peer Deps | Auto-install | ต้อง manual | Strict (ต้องระบุ) | Auto-install |
| Ecosystem | ใหญ่ที่สุด (default) | ใหญ่ | โตเร็วมาก | กำลังโต |
| Maturity | สูงสุด (2010+) | สูง (2016+) | สูง (2017+) | ปานกลาง (2022+) |
Content-Addressable Storage — ทำไม pnpm ประหยัด Disk?
# npm: ทุก Project copy packages เป็นของตัวเอง
# Project A: node_modules/react (15MB)
# Project B: node_modules/react (15MB) ← copy ซ้ำ!
# Project C: node_modules/react (15MB) ← copy ซ้ำ!!
# Total: 45MB สำหรับ React 3 ชุด (เนื้อหาเหมือนกัน!)
# pnpm: เก็บ Package เดียวใน Content Store แล้วใช้ Hard link
# ~/.pnpm-store/react@19.0.0/... (15MB) ← เก็บครั้งเดียว!
# Project A: node_modules/.pnpm/react@19.0.0 → Hard link ไป Store
# Project B: node_modules/.pnpm/react@19.0.0 → Hard link ไป Store
# Project C: node_modules/.pnpm/react@19.0.0 → Hard link ไป Store
# Total: ~15MB สำหรับ React ไม่ว่าจะมีกี่ Project!
# ตรวจสอบ Store location:
pnpm store path
# → C:\Users\PC\AppData\Local\pnpm\store\v3
# ตรวจสอบ Store size:
pnpm store status
Strict node_modules Structure
# npm (flat/hoisted):
# node_modules/
# react/ ← dependencies ถูก hoist ขึ้นมา
# react-dom/
# lodash/ ← อาจเข้าถึงได้แม้ไม่ได้ depend!
# some-deep-dep/ ← phantom dependency!
# pnpm (strict/non-flat):
# node_modules/
# .pnpm/ ← ทุก Package อยู่ที่นี่
# react@19.0.0/
# react-dom@19.0.0/
# lodash@4.17.21/
# react → .pnpm/react@19.0.0/node_modules/react ← symlink!
# react-dom → .pnpm/react-dom@19.0.0/... ← symlink!
# # lodash ไม่มี symlink ที่ root ถ้าไม่ได้ depend!
# # → ป้องกัน Phantom Dependencies!
# ข้อดี: ไม่สามารถ import Package ที่ไม่ได้ declare ใน package.json
# ข้อเสีย: บาง Package เก่าอาจพัง (แก้ด้วย shamefully-hoist)
ติดตั้ง pnpm
# วิธีที่ 1: Corepack (แนะนำ — มากับ Node.js)
corepack enable
corepack prepare pnpm@latest --activate
# วิธีที่ 2: npm
npm install -g pnpm
# วิธีที่ 3: Standalone Script
# Windows (PowerShell):
iwr https://get.pnpm.io/install.ps1 -useb | iex
# macOS/Linux:
curl -fsSL https://get.pnpm.io/install.sh | sh -
# ตรวจสอบ version:
pnpm --version
# 9.x.x (2026)
คำสั่ง pnpm ที่ใช้บ่อย
# Install ทุก dependencies จาก package.json
pnpm install
# หรือ
pnpm i
# เพิ่ม Package
pnpm add react react-dom # dependencies
pnpm add -D typescript @types/node # devDependencies
pnpm add -g tsx # global
# ลบ Package
pnpm remove lodash
pnpm rm lodash # shorthand
# Update Package
pnpm update # update ทุกตัวตาม semver range
pnpm update react # update เฉพาะ react
pnpm update --latest # update ทุกตัวเป็น latest (ไม่สน semver range)
# รัน Script
pnpm run build
pnpm run dev
pnpm start # pnpm run start shorthand
pnpm test # pnpm run test shorthand
# pnpm dlx (เหมือน npx)
pnpm dlx create-next-app@latest my-app
pnpm dlx degit user/repo my-project
pnpm dlx typescript --init
# ดู Dependencies Tree
pnpm list
pnpm list --depth=2
pnpm why react # ดูว่าทำไมถึงมี react (ใครต้องการ)
# ตรวจสอบ outdated
pnpm outdated
pnpm-lock.yaml — Lockfile
# pnpm-lock.yaml คือ Lockfile ที่บันทึก exact version ของทุก dependency
# ต้อง commit เข้า Git เสมอ!
#
# ข้อดีของ pnpm-lock.yaml:
# - Readable (YAML format) ไม่เหมือน package-lock.json (JSON ยาวมาก)
# - Deterministic: install ที่ไหนก็ได้ผลเหมือนกัน
# - เร็วกว่าในการ parse
# ห้ามแก้ pnpm-lock.yaml ด้วยมือ!
# ให้ pnpm จัดการเอง
# Frozen lockfile (CI):
pnpm install --frozen-lockfile
# → ถ้า lockfile ไม่ตรงกับ package.json จะ error ทันที!
pnpm Workspace — Monorepo
# Monorepo Structure:
# my-monorepo/
# pnpm-workspace.yaml ← กำหนดว่า package อยู่ที่ไหน
# package.json ← root package.json
# packages/
# shared-ui/
# package.json ← @myorg/shared-ui
# web-app/
# package.json ← @myorg/web-app
# api-server/
# package.json ← @myorg/api-server
# pnpm-workspace.yaml:
packages:
- 'packages/*'
- 'apps/*'
- '!**/test/**' # exclude test directories
# Install ทั้ง Workspace:
pnpm install # ติดตั้ง dependencies ทุก package ในครั้งเดียว
# เพิ่ม dependency ให้ package เฉพาะ:
pnpm add react --filter @myorg/web-app
pnpm add express --filter @myorg/api-server
# Internal dependency (package ใน workspace reference กัน):
# packages/web-app/package.json:
{
"dependencies": {
"@myorg/shared-ui": "workspace:*" ← reference ภายใน workspace
}
}
# รัน Script ใน package เฉพาะ:
pnpm --filter @myorg/web-app dev
pnpm --filter @myorg/api-server build
# รัน Script ทุก package:
pnpm -r run build # recursive: build ทุก package
pnpm -r run test # recursive: test ทุก package
# รัน Script เฉพาะ package ที่เปลี่ยน:
pnpm -r --filter "...[origin/main]" run build
Filtering Packages
# --filter patterns:
pnpm --filter @myorg/web-app build # ชื่อ package
pnpm --filter "./packages/**" build # glob path
pnpm --filter "...@myorg/shared-ui" build # shared-ui + ทุก package ที่ depend มัน
pnpm --filter "@myorg/web-app..." build # web-app + ทุก dependency ของมัน
pnpm --filter "...[origin/main]" build # เฉพาะ package ที่เปลี่ยนจาก main
# ตัวอย่าง CI/CD:
# Build เฉพาะ package ที่ถูกแก้ไข (เร็วมากใน PR):
pnpm -r --filter "...[HEAD~1]" run build
pnpm -r --filter "...[HEAD~1]" run test
pnpm overrides — แก้ปัญหา Dependency
# package.json:
{
"pnpm": {
"overrides": {
"lodash": "4.17.21", // force version
"minimist@<1.2.6": ">=1.2.6", // patch vulnerability
"foo>bar": "2.0.0" // override nested dependency
},
"peerDependencyRules": {
"ignoreMissing": ["@babel/*"], // ไม่ warn เรื่อง peer deps
"allowAny": ["eslint"] // อนุญาตทุก version
}
}
}
pnpm Store — จัดการ Global Store
# ดู Store path:
pnpm store path
# ล้าง Package ที่ไม่ได้ใช้แล้ว (orphaned):
pnpm store prune
# → ลบ Package ที่ไม่มี Project ไหนใช้แล้ว (ประหยัด Disk!)
# ตรวจสอบ Store integrity:
pnpm store status
# ย้าย Store location:
# ตั้งค่าใน .npmrc:
store-dir=/custom/path/to/pnpm-store
Migration จาก npm/yarn
# จาก npm:
# 1. ลบ node_modules และ package-lock.json
rm -rf node_modules package-lock.json
# 2. ติดตั้งด้วย pnpm
pnpm import # อ่าน package-lock.json แล้วสร้าง pnpm-lock.yaml
# หรือ
pnpm install # สร้าง pnpm-lock.yaml ใหม่
# จาก yarn:
rm -rf node_modules yarn.lock
pnpm import # อ่าน yarn.lock แล้วสร้าง pnpm-lock.yaml
# ถ้ามี Package ที่พังเพราะ strict node_modules:
# .npmrc:
shamefully-hoist=true # hoist แบบ npm (ไม่แนะนำ ใช้เป็นทางแก้ชั่วคราว)
# หรือ
public-hoist-pattern[]=*types*
public-hoist-pattern[]=*eslint*
Performance Benchmarks
| Benchmark (Next.js project) | npm | yarn (v4) | pnpm | bun |
|---|---|---|---|---|
| Cold install (no cache) | ~45s | ~30s | ~18s | ~12s |
| Warm install (with cache) | ~15s | ~8s | ~4s | ~3s |
| With lockfile only | ~12s | ~5s | ~3s | ~2s |
| node_modules size | ~350MB | ~320MB | ~180MB | ~300MB |
| Global disk (10 projects) | ~3.5GB | ~3.2GB | ~500MB | ~3.0GB |
pnpm ประหยัด Disk ที่สุด (content-addressable store) โดยเฉพาะเมื่อมีหลาย Project ใน 10 Project ที่ใช้ dependencies คล้ายกัน npm ใช้ ~3.5GB แต่ pnpm ใช้แค่ ~500MB!
pnpm + Changesets — Version Management
# Changesets ช่วยจัดการ Version ใน Monorepo
pnpm add -Dw @changesets/cli
# Initialize:
pnpm changeset init
# เมื่อแก้ไข Package:
pnpm changeset
# → เลือก Package ที่เปลี่ยน
# → เลือก Bump type (patch/minor/major)
# → เขียน Summary
# Bump versions:
pnpm changeset version
# → update package.json + CHANGELOG.md
# Publish:
pnpm changeset publish
# → publish ทุก Package ที่ version เปลี่ยน
pnpm ใน CI/CD (Caching)
# GitHub Actions:
name: CI
on: [push, pull_request]
jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: pnpm/action-setup@v4
with:
version: 9
- uses: actions/setup-node@v4
with:
node-version: 22
cache: 'pnpm' # ← cache pnpm store อัตโนมัติ!
- run: pnpm install --frozen-lockfile
- run: pnpm -r run build
- run: pnpm -r run test
# GitLab CI:
stages:
- install
- build
- test
install:
stage: install
cache:
key:
files:
- pnpm-lock.yaml
paths:
- .pnpm-store/
script:
- corepack enable
- corepack prepare pnpm@latest --activate
- pnpm config set store-dir .pnpm-store
- pnpm install --frozen-lockfile
.npmrc — pnpm Configuration
# .npmrc ใน root ของ project:
# Store location (optional):
store-dir=~/.pnpm-store
# Strict peer dependencies (default in pnpm):
strict-peer-dependencies=true
# Auto-install peer deps:
auto-install-peers=true
# Hoist patterns (สำหรับ Package ที่ต้อง hoist):
public-hoist-pattern[]=*types*
public-hoist-pattern[]=*eslint*
public-hoist-pattern[]=*prettier*
# Shamefully hoist (ไม่แนะนำ):
# shamefully-hoist=true
# Node.js version:
use-node-version=22.0.0
# Registry:
registry=https://registry.npmjs.org/
# Lockfile:
lockfile=true
# Side effects cache:
side-effects-cache=true
pnpm Best Practices
| Practice | ทำไม |
|---|---|
| ใช้ Corepack enable | จัดการ pnpm version ตาม project (packageManager field) |
| Commit pnpm-lock.yaml | Deterministic builds ทุก environment ได้ผลเหมือนกัน |
| CI ใช้ --frozen-lockfile | ป้องกัน lockfile ไม่ตรงกับ package.json |
| ใช้ workspace:* สำหรับ internal packages | Reference ภายใน monorepo แบบ live (ไม่ต้อง publish) |
| pnpm store prune เป็นระยะ | ลบ Package ที่ไม่ใช้แล้ว ประหยัด Disk |
| ไม่ใช้ shamefully-hoist | ปล่อยให้ pnpm บังคับ strict dependencies (ดีกว่า) |
| ใช้ --filter ใน CI | Build/Test เฉพาะ package ที่เปลี่ยน (เร็วมาก) |
| ตั้ง packageManager ใน package.json | บังคับทุกคนในทีมใช้ pnpm version เดียวกัน |
corepack enable && pnpm init แล้วเริ่มใช้ pnpm add/install/run แทน npm ได้ทันที Migration จาก npm ง่ายมาก แค่ลบ node_modules + package-lock.json แล้วรัน pnpm install
สรุป
pnpm เป็น Package Manager ที่ เร็วกว่าและประหยัด Disk กว่า npm อย่างมาก ด้วย Content-Addressable Storage ที่เก็บ Package เพียงครั้งเดียว ใช้ Hard link แทนการ Copy ทำให้ 10 Project ใช้ Disk เท่ากับ 1 Project เหมาะกับ Monorepo ที่มีหลาย Package ใน Workspace เดียว รองรับ Filtering สำหรับ CI/CD ที่ Build/Test เฉพาะ Package ที่เปลี่ยน
pnpm ยังมี Strict node_modules ที่ป้องกัน Phantom Dependencies ทำให้ Code ที่เขียนถูกต้องจริง ๆ ไม่ใช่บังเอิญใช้ได้เพราะ hoisting ถ้าทีมยังใช้ npm อยู่ ลองเปลี่ยนมา pnpm จะเห็นความแตกต่างทันที
