Frontend Build Tools & Package Managers in 2026 — The Complete Comparison
A bilingual comparison of frontend build tools (Webpack, Vite, Rollup, esbuild, Turbopack, SWC, Bun) and package managers (npm, Yarn, pnpm, Bun) — architecture, speed, disk usage, ecosystem, and when to use each.
The Build Tool Landscape Has Exploded {Bối cảnh Build Tool đã bùng nổ}
In 2020, the choice was simple {Năm 2020, lựa chọn rất đơn giản}: Webpack for apps, Rollup for libraries {Webpack cho app, Rollup cho thư viện}. In 2026, we have 10+ serious contenders {chúng ta có hơn 10 ứng viên nghiêm túc}, written in JavaScript, Go, and Rust {viết bằng JavaScript, Go, và Rust}.
This post covers every major build tool {Bài viết này bao gồm mọi build tool chính}, what they’re good at, and when to choose them {chúng giỏi gì, và khi nào nên chọn chúng}. Then Part 2 does the same for the layer underneath — the package managers (npm, Yarn, pnpm, Bun) that install the dependencies your build tool bundles {Sau đó Phần 2 làm điều tương tự cho lớp bên dưới — các package manager (npm, Yarn, pnpm, Bun) cài đặt dependency mà build tool của bạn đem đi bundle}.
Two different jobs, often confused {Hai công việc khác nhau, hay bị nhầm}: a build tool turns your source into shippable output; a package manager downloads and links the dependencies into
node_modules. You need one of each {một build tool biến source của bạn thành output xuất xưởng; một package manager tải và liên kết dependency vàonode_modules. Bạn cần mỗi loại một cái}.
Quick Overview Table {Bảng tổng quan nhanh}
| Tool | Language {Ngôn ngữ} | Category {Phân loại} | Best for {Tốt nhất cho} |
|---|---|---|---|
| Webpack 5 | JavaScript | Bundler | Large legacy apps, complex configs {App lớn cũ, config phức tạp} |
| Vite 6 | JavaScript + Rust (via Rolldown) | Dev server + Bundler | Modern web apps (React, Vue, Svelte, Astro) |
| Rollup 4 | JavaScript | Bundler | Libraries, ES modules |
| Rolldown | Rust | Bundler | Vite’s production bundler (replacing Rollup) {Bundler production của Vite} |
| esbuild | Go | Bundler + Transpiler | Ultra-fast builds, simple apps, tooling infra |
| Turbopack | Rust | Bundler | Next.js (exclusive) |
| Parcel 2 | Rust + JavaScript | Bundler | Zero-config projects, prototypes |
| SWC | Rust | Transpiler + Minifier | Replacing Babel/Terser in other tools |
| Bun | Zig + C++ | Runtime + Bundler | Full-stack JS, fast scripts |
| tsup | JavaScript (wraps esbuild) | Library bundler | TypeScript libraries {Thư viện TypeScript} |
| unbuild | JavaScript (wraps Rollup) | Library bundler | Universal libraries (CJS + ESM) |
Core Concepts You Need First {Khái niệm cốt lõi cần nắm trước}
People say “build tool” to mean very different jobs {Người ta gọi “build tool” cho nhiều việc rất khác nhau}. Separating them makes every comparison below click {Tách chúng ra thì mọi so sánh bên dưới sẽ rõ ngay}:
Your source code (.tsx, .ts, .css, .svg ...)
│
▼
┌──────────────┐ 1. TRANSPILE — strip types, JSX → JS, modern → target JS
│ Transpiler │ (Babel, SWC, esbuild, tsc)
└──────┬───────┘
▼
┌──────────────┐ 2. BUNDLE — resolve imports, build one dependency graph,
│ Bundler │ tree-shake, code-split into chunks
└──────┬───────┘ (Webpack, Rollup, Rolldown, esbuild, Parcel, Turbopack)
▼
┌──────────────┐ 3. MINIFY — shrink output (rename vars, drop whitespace,
│ Minifier │ dead-code elimination) (Terser, SWC, esbuild)
└──────┬───────┘
▼
dist/ output
Separate concern → DEV SERVER + HMR: serve files while you code and
hot-swap only what changed (Vite, Webpack dev server, Turbopack).
A single tool may do one job or all of them {Một tool có thể làm một việc hoặc tất cả}. esbuild does 1+2+3 {esbuild làm 1+2+3}; SWC does 1+3 {SWC làm 1+3}; Rollup does 2 {Rollup làm 2}; Vite orchestrates everything plus the dev server {Vite điều phối tất cả cộng thêm dev server}.
ESM vs CJS — why it matters {ESM vs CJS — vì sao quan trọng}
Bundlers care deeply about module format {Bundler rất quan tâm tới định dạng module}. ESM (import/export) is statically analyzable {ESM phân tích tĩnh được}, so the bundler knows exactly what is used {nên bundler biết chính xác cái gì được dùng}. CJS (require) is dynamic {CJS động}, which blocks tree-shaking {nên chặn tree-shaking}.
ESM (static) CJS (dynamic)
import { a } from './m'; const { a } = require('./m');
→ bundler sees "only a is used" → could require anything at runtime
→ b, c, d are tree-shaken away → whole module kept "just in case"
Tree-shaking in one picture {Tree-shaking trong một hình}
Tree-shaking removes exports nobody imports {Tree-shaking loại bỏ export không ai import}:
utils.js exports: [ format, parse, debounce, throttle ]
app.js imports: [ format ]
│
▼ tree-shake
final bundle keeps: [ format ] ← parse/debounce/throttle dropped
Dev server & HMR {Dev server & HMR}
HMR (Hot Module Replacement) swaps a changed module without a full reload, keeping app state {HMR thay module đã đổi mà không reload toàn trang, giữ nguyên state}. This is the single biggest day-to-day DX factor {Đây là yếu tố DX hằng ngày lớn nhất}, and where native-speed tools win hardest {và là chỗ tool tốc-độ-native thắng đậm nhất}.
A Short Timeline {Dòng thời gian ngắn}
How we got from “one obvious choice” to “ten contenders” {Làm sao ta đi từ “một lựa chọn hiển nhiên” tới “mười ứng viên”}:
2012 ──● Webpack — "everything is a module", loaders/plugins era
│
2015 ──● Rollup — ESM-first, tree-shaking, the library bundler
│
2016 ──● Babel everywhere — JS transpilation standard (but slow)
│
2020 ──● esbuild (Go) — 10-100x faster, sparks the native rewrite wave
│● Snowpack/Vite — "no-bundle" dev with native ESM
│
2021 ──● SWC (Rust) — Babel/Terser killer, adopted by Next.js
│
2022 ──● Turbopack (Rust) — Vercel's incremental bundler for Next.js
│
2023 ──● Bun (Zig) — runtime + bundler + package manager in one
│
2024 ──● Rolldown (Rust) — Rollup rewrite, becomes Vite's prod engine
│
2026 ──● Convergence — Vite/Rolldown is the default; JS-based tools fade
The throughline {Mạch xuyên suốt}: every winning tool of the last five years is written in a systems language (Go, Rust, Zig) {mọi tool thắng cuộc 5 năm qua đều viết bằng ngôn ngữ hệ thống (Go, Rust, Zig)}.
Webpack 5 {Webpack 5}
Architecture {Kiến trúc}
The original king of bundlers {Vua bundler nguyên thuỷ}. Webpack treats everything as a module {Webpack xem mọi thứ là module} — JS, CSS, images, fonts — and builds a dependency graph {và xây dựng đồ thị phụ thuộc}.
Entry → Loaders (transform) → Plugins (optimize) → Output chunks
↑ ↑
babel-loader TerserPlugin
css-loader MiniCssExtractPlugin
file-loader HtmlWebpackPlugin
Strengths {Điểm mạnh}
- Ecosystem {Hệ sinh thái}: thousands of loaders and plugins for any use case {hàng nghìn loader và plugin cho mọi trường hợp}
- Code splitting: the most mature and configurable {trưởng thành và tuỳ chỉnh được nhất}
- Module Federation: micro-frontend architecture built-in {kiến trúc micro-frontend tích hợp sẵn}
- Stability {Ổn định}: battle-tested in millions of production apps {đã thử nghiệm trong hàng triệu app production}
- HMR: works with any framework {hoạt động với mọi framework}
Weaknesses {Điểm yếu}
- Speed {Tốc độ}: slowest bundler in 2026 {bundler chậm nhất 2026}. Large projects: 30-120s cold builds {Project lớn: 30-120s build lạnh}
- Configuration {Cấu hình}: notoriously complex {phức tạp khét tiếng}. A typical config is 100-300 lines {Config thường 100-300 dòng}
- Dev experience {Trải nghiệm dev}: slow startup, slow HMR on large codebases {khởi động chậm, HMR chậm trên codebase lớn}
- Mental model {Mô hình tư duy}: loader chains, plugin hooks — steep learning curve {chuỗi loader, hook plugin — đường cong học dốc}
When to use {Khi nào dùng}
- Maintaining existing large apps that already use Webpack {Bảo trì app lớn đã dùng Webpack}
- Need Module Federation for micro-frontends {Cần Module Federation cho micro-frontend}
- Complex custom build pipelines that no other tool supports {Pipeline build tuỳ chỉnh phức tạp mà không tool nào khác hỗ trợ}
When NOT to use {Khi nào KHÔNG dùng}
- New projects in 2026 {Project mới năm 2026} — Vite or Turbopack (for Next.js) are strictly better {Vite hoặc Turbopack (cho Next.js) tốt hơn hoàn toàn}
- If build speed matters (it always does) {Nếu tốc độ build quan trọng (luôn quan trọng)}
// webpack.config.js — typical setup
const path = require('path');
const HtmlWebpackPlugin = require('html-webpack-plugin');
module.exports = {
entry: './src/index.tsx',
output: {
path: path.resolve(__dirname, 'dist'),
filename: '[name].[contenthash].js',
clean: true,
},
module: {
rules: [
{
test: /\.tsx?$/,
use: 'swc-loader', // replaced babel-loader
exclude: /node_modules/,
},
{
test: /\.css$/,
use: ['style-loader', 'css-loader', 'postcss-loader'],
},
],
},
plugins: [new HtmlWebpackPlugin({ template: './index.html' })],
resolve: { extensions: ['.tsx', '.ts', '.js'] },
};
Vite 6 {Vite 6}
Architecture {Kiến trúc}
Vite takes a fundamentally different approach {Vite có cách tiếp cận khác căn bản}: no bundling in development {không bundle khi development}. It serves native ES modules directly to the browser {Nó phục vụ ES module native trực tiếp cho browser}, using the browser’s own import system {dùng hệ thống import của chính browser}.
Development:
Source files → esbuild (dep pre-bundling) → Native ESM → Browser
HMR: only re-serves the changed module
Production:
Source files → Rolldown (Rust bundler) → Optimized chunks
Strengths {Điểm mạnh}
- Instant dev startup {Khởi động dev tức thì}: no bundling, just serve files {không bundle, chỉ serve file}. Even 10K+ module apps start in < 500ms {App hơn 10K module cũng khởi động dưới 500ms}
- Lightning HMR {HMR siêu nhanh}: typically under 50ms regardless of app size {thường dưới 50ms bất kể app size}
- Simple config {Cấu hình đơn giản}: 10-30 lines for most projects {10-30 dòng cho hầu hết project}
- Plugin ecosystem {Hệ sinh thái plugin}: compatible with Rollup plugins {tương thích plugin Rollup}
- Framework agnostic {Không phụ thuộc framework}: React, Vue, Svelte, Solid, Astro, Qwik all use Vite {đều dùng Vite}
- CSS support: PostCSS, Tailwind, CSS Modules, preprocessors — all built-in {tất cả tích hợp sẵn}
Weaknesses {Điểm yếu}
- Dev/prod parity {Đồng nhất dev/prod}: historically, dev (native ESM) and prod (Rollup bundle) could behave differently {trước đây, dev và prod có thể hoạt động khác nhau}. Rolldown integration fixes this {Tích hợp Rolldown sửa vấn đề này}
- Large dependency pre-bundling {Pre-bundling dependency lớn}: first cold start can be slow if many deps need pre-bundling {Lần chạy lạnh đầu có thể chậm nếu nhiều dep cần pre-bundle}
- SSR complexity {Phức tạp SSR}: SSR pipeline is less mature than Next.js/Webpack’s {Pipeline SSR ít trưởng thành hơn Next.js/Webpack}
When to use {Khi nào dùng}
- Most new projects in 2026 {Hầu hết project mới năm 2026} — this is the default choice {đây là lựa chọn mặc định}
- React (with React Router or TanStack Router), Vue, Svelte, Solid, Astro
- Any project that values developer experience {Bất kỳ project nào coi trọng trải nghiệm developer}
When NOT to use {Khi nào KHÔNG dùng}
- Next.js projects (use Turbopack) {Project Next.js (dùng Turbopack)}
- Need Module Federation (Webpack’s territory) {Cần Module Federation}
// vite.config.ts — typical setup
import { defineConfig } from 'vite';
import react from '@vitejs/plugin-react-swc';
export default defineConfig({
plugins: [react()],
build: {
sourcemap: true,
rollupOptions: {
output: { manualChunks: { vendor: ['react', 'react-dom'] } },
},
},
});
Rollup 4 {Rollup 4}
Architecture {Kiến trúc}
Rollup is the ES module-native bundler {Rollup là bundler native ES module}. It was the first bundler designed around import/export {Nó là bundler đầu tiên thiết kế xung quanh import/export} rather than treating modules as an afterthought {thay vì xem module là thứ phụ}.
ES Module sources → Tree-shaking → Scope hoisting → Output (ESM/CJS/UMD)
Strengths {Điểm mạnh}
- Best tree-shaking {Tree-shaking tốt nhất}: designed from ground up for dead code elimination {thiết kế từ đầu cho loại bỏ code chết}
- Smallest output {Output nhỏ nhất}: scope hoisting produces the most compact bundles {scope hoisting tạo bundle nhỏ gọn nhất}
- Multiple output formats {Nhiều định dạng output}: ESM, CJS, UMD, IIFE from same source {từ cùng source}
- Clean plugin API {Plugin API sạch}: simpler and more composable than Webpack {đơn giản và có thể kết hợp hơn Webpack}
- Battle-tested for libraries {Đã thử nghiệm cho thư viện}: most npm packages are built with Rollup {hầu hết package npm được build bằng Rollup}
Weaknesses {Điểm yếu}
- Slow for large apps {Chậm cho app lớn}: JavaScript-based, doesn’t parallelize well {Dựa trên JavaScript, không song song hoá tốt}
- No dev server {Không có dev server}: needs additional tooling (hence Vite wrapping it) {cần công cụ thêm (vì vậy Vite bọc nó)}
- HMR: not built-in {không tích hợp sẵn}
- Non-ESM transforms: needs plugins for CommonJS, JSON, etc. {cần plugin cho CommonJS, JSON}
When to use {Khi nào dùng}
- Building npm libraries {Xây dựng thư viện npm} that need small output
- Need ESM + CJS dual package output {Cần output ESM + CJS đồng thời}
- Using Vite (which uses Rollup under the hood for production) {Dùng Vite (sử dụng Rollup bên dưới cho production)}
When NOT to use {Khi nào KHÔNG dùng}
- Application bundling (use Vite instead) {Bundle app (dùng Vite thay thế)}
- Need fast iteration (no dev server/HMR) {Cần lặp nhanh}
// rollup.config.js — a typical library setup
import resolve from '@rollup/plugin-node-resolve';
import commonjs from '@rollup/plugin-commonjs';
import esbuild from 'rollup-plugin-esbuild'; // fast TS transpile
export default {
input: 'src/index.ts',
output: [
{ file: 'dist/index.cjs', format: 'cjs' },
{ file: 'dist/index.mjs', format: 'esm' },
],
external: ['react'], // don't bundle peer deps into a library
plugins: [resolve(), commonjs(), esbuild({ minify: true })],
};
Rolldown {Rolldown}
Architecture {Kiến trúc}
Rolldown is the Rust rewrite of Rollup {Rolldown là Rollup viết lại bằng Rust}, created by the Vite team {được tạo bởi team Vite}. It aims to be a drop-in replacement for Rollup with dramatically better performance {Nó nhắm thay thế Rollup hoàn toàn với performance tốt hơn đáng kể}.
Rollup API (JavaScript) → Rolldown Core (Rust) → Same output quality
10-30x faster
Strengths {Điểm mạnh}
- Rollup-compatible API {API tương thích Rollup}: most Rollup plugins work unchanged {hầu hết plugin Rollup hoạt động không thay đổi}
- 10-30x faster than Rollup {Nhanh hơn Rollup 10-30 lần}: Rust parallelism + zero-copy parsing {Song song Rust + phân tích zero-copy}
- Built-in transforms {Transform tích hợp}: TypeScript, JSX, minification — no extra plugins needed {không cần plugin thêm}
- Unified dev/prod {Đồng nhất dev/prod}: same bundler for both in Vite {cùng bundler cho cả hai trong Vite}
- Tree-shaking: same quality as Rollup {chất lượng như Rollup}
Weaknesses {Điểm yếu}
- Newer ecosystem {Hệ sinh thái mới}: not all Rollup plugins fully compatible yet {chưa phải tất cả plugin Rollup đều tương thích}
- Tied to Vite {Gắn với Vite}: primarily designed as Vite’s engine, standalone use is secondary {chủ yếu thiết kế cho Vite, dùng độc lập là phụ}
- Evolving API {API đang phát triển}: some edge cases still being resolved {một số edge case vẫn đang giải quyết}
When to use {Khi nào dùng}
- You use Vite → Rolldown is already your production bundler {Bạn dùng Vite → Rolldown đã là bundler production của bạn}
- Building libraries that previously used Rollup and need faster builds {Xây thư viện trước dùng Rollup và cần build nhanh hơn}
You rarely install Rolldown directly {Bạn hiếm khi cài Rolldown trực tiếp} — you opt into it through Vite {bạn chọn nó thông qua Vite}:
# Use the Rolldown-powered Vite (the default engine going forward)
npm install vite@latest
# or, to try it standalone as a Rollup drop-in:
npm install -D rolldown
// rolldown.config.js — note the API mirrors Rollup almost 1:1
import { defineConfig } from 'rolldown';
export default defineConfig({
input: 'src/index.ts',
output: { dir: 'dist', format: 'esm' },
// TypeScript/JSX transforms are built in — no extra plugins
});
esbuild {esbuild}
Architecture {Kiến trúc}
Written in Go {Viết bằng Go}, esbuild was the tool that started the “fast bundler” revolution {esbuild là tool bắt đầu cuộc cách mạng “bundler nhanh”}. It does bundling, transpiling, and minification in a single pass {Nó bundle, transpile, và minify trong một lượt duy nhất}.
Source → Parse (parallel) → Link → Tree-shake → Code-gen → Output
All in Go, all in memory, all parallel
Strengths {Điểm mạnh}
- Extreme speed {Tốc độ cực nhanh}: 10-100x faster than Webpack {nhanh hơn Webpack 10-100 lần}. Bundles React in 40ms {Bundle React trong 40ms}
- Zero config for simple cases {Không config cho case đơn giản}
- Built-in TypeScript + JSX {TypeScript + JSX tích hợp}: no babel needed {không cần babel}
- Minification: fastest minifier available (replaces Terser) {minifier nhanh nhất (thay thế Terser)}
- CSS bundling: basic but fast {cơ bản nhưng nhanh}
- Infrastructure role {Vai trò hạ tầng}: powers Vite’s dep pre-bundling, tsup, many other tools {cung cấp sức mạnh cho Vite dep pre-bundling, tsup, nhiều tool khác}
Weaknesses {Điểm yếu}
- Limited plugin API {Plugin API hạn chế}: can’t do everything Webpack/Rollup plugins can {không làm được mọi thứ plugin Webpack/Rollup có thể}
- No HMR {Không có HMR}: it’s a bundler, not a dev server {nó là bundler, không phải dev server}
- Basic code splitting {Code splitting cơ bản}: less sophisticated than Webpack or Rollup {ít tinh vi hơn Webpack hoặc Rollup}
- No type checking {Không kiểm tra type}: strips types but doesn’t validate them {xoá type nhưng không xác thực}
- CSS limitations {Giới hạn CSS}: no PostCSS, no CSS Modules (without plugins) {không có PostCSS, CSS Modules}
When to use {Khi nào dùng}
- Simple apps/scripts {App/script đơn giản} that don’t need complex transforms {không cần transform phức tạp}
- Build tooling infrastructure {Hạ tầng build tooling}: as a fast transpiler/minifier inside other tools {làm transpiler/minifier nhanh bên trong tool khác}
- CI pipelines {Pipeline CI}: when build speed is critical {khi tốc độ build quan trọng}
- TypeScript libraries via tsup (esbuild wrapper) {Thư viện TypeScript qua tsup}
When NOT to use {Khi nào KHÔNG dùng}
- Need HMR/dev server (use Vite which uses esbuild internally) {Cần HMR/dev server}
- Complex CSS pipeline (PostCSS, Tailwind) {Pipeline CSS phức tạp}
- Need advanced code splitting {Cần code splitting nâng cao}
# esbuild — one command does it all
esbuild src/index.tsx --bundle --minify --sourcemap --outdir=dist --format=esm
# Finishes in ~50ms for most projects
Turbopack {Turbopack}
Architecture {Kiến trúc}
Built by Vercel in Rust {Được Vercel xây bằng Rust}, Turbopack is the Next.js-exclusive bundler {là bundler chỉ dành cho Next.js} designed to replace Webpack in the Next.js ecosystem {thiết kế để thay thế Webpack trong hệ sinh thái Next.js}.
Source → Turbo Engine (incremental computation) → Optimized output
Function-level caching, maximal parallelism
Only recomputes what changed
Strengths {Điểm mạnh}
- Incremental by design {Thiết kế tăng dần}: function-level caching means only changed code is reprocessed {cache cấp hàm nghĩa là chỉ code thay đổi được xử lý lại}
- Fast HMR {HMR nhanh}: maintains state better than Webpack for Next.js {giữ state tốt hơn Webpack cho Next.js}
- Optimized for React Server Components {Tối ưu cho React Server Component}
- Deep Next.js integration {Tích hợp sâu Next.js}: understands App Router, Server Actions, etc.
- Production builds (Next.js 16): stable for both dev and prod {ổn định cho cả dev và prod}
Weaknesses {Điểm yếu}
- Next.js only {Chỉ Next.js}: cannot be used with other frameworks {không thể dùng với framework khác}
- Not standalone {Không độc lập}: no CLI, no separate npm package for general use {không có CLI, không có package npm riêng cho sử dụng chung}
- Less configurable {Ít tuỳ chỉnh}: intentionally opinionated for Next.js {có chủ đích opinionated cho Next.js}
- Webpack plugin incompatibility {Không tương thích plugin Webpack}: most Webpack plugins don’t work {hầu hết plugin Webpack không hoạt động}
- Closed ecosystem {Hệ sinh thái đóng}: tied to Vercel’s roadmap {gắn với lộ trình Vercel}
When to use {Khi nào dùng}
- You use Next.js → Turbopack is the default since v16 {Bạn dùng Next.js → Turbopack là mặc định từ v16}
When NOT to use {Khi nào KHÔNG dùng}
- Any non-Next.js project {Bất kỳ project không phải Next.js}
Parcel 2 {Parcel 2}
Architecture {Kiến trúc}
Parcel’s philosophy is zero configuration {Triết lý của Parcel là không cần cấu hình}. It auto-detects everything from your source files {Nó tự động phát hiện mọi thứ từ file source}: framework, preprocessors, targets {framework, preprocessor, target}.
Source → Asset Graph (auto-detected) → Transforms (parallel, Rust) → Packager → Output
HTML entry points SWC for JS/TS
package.json targets PostCSS for CSS
browserslist for compat Image optimization
Strengths {Điểm mạnh}
- True zero-config {Thực sự không cần config}: point at an HTML file, it figures out the rest {trỏ vào file HTML, nó tự tìm ra phần còn lại}
- Fast: uses Rust (SWC) for transforms, parallel processing {dùng Rust (SWC) cho transform, xử lý song song}
- Multi-target: can output for multiple environments from one build {có thể output cho nhiều môi trường từ một build}
- Built-in everything {Mọi thứ tích hợp}: PostCSS, image optimization, tree-shaking, code splitting, HMR
- HTML entry points {Điểm vào HTML}: unlike most bundlers that start from JS {khác hầu hết bundler bắt đầu từ JS}
Weaknesses {Điểm yếu}
- Smaller ecosystem {Hệ sinh thái nhỏ hơn}: fewer plugins than Webpack or Vite {ít plugin hơn Webpack hoặc Vite}
- Less adoption {Ít được sử dụng}: most frameworks chose Vite as their default {hầu hết framework chọn Vite làm mặc định}
- Config when needed {Config khi cần}: when you DO need customization, the escape hatches are less documented {khi cần tuỳ chỉnh, giải pháp thay thế ít tài liệu hơn}
- Less predictable {Ít dự đoán được}: auto-detection magic can surprise you {phép thuật tự phát hiện có thể gây bất ngờ}
When to use {Khi nào dùng}
- Prototypes, demos, quick experiments {Prototype, demo, thử nghiệm nhanh}
- Vanilla JS/TS projects without a framework {Project vanilla JS/TS không dùng framework}
- When you want zero setup time {Khi bạn muốn không mất thời gian setup}
When NOT to use {Khi nào KHÔNG dùng}
- Large team projects (less ecosystem, less community knowledge) {Project team lớn}
- Framework-based apps (React/Vue/Svelte) — frameworks prefer Vite {App dựa trên framework — framework ưu tiên Vite}
# Parcel — literally just this
parcel src/index.html
# Auto-detects TypeScript, React, PostCSS, everything
SWC {SWC}
Architecture {Kiến trúc}
SWC is not a bundler — it’s a Rust-based compiler {SWC không phải bundler — nó là compiler dựa trên Rust} that replaces Babel and Terser {thay thế Babel và Terser}. It does transpilation, minification, and soon more {Nó làm transpilation, minification, và sắp thêm nữa}.
Source → SWC Core (Rust) → Transformed output
TypeScript → JavaScript
JSX → createElement calls
Modern JS → Target-compatible JS
Minification (replacing Terser)
Strengths {Điểm mạnh}
- 20-70x faster than Babel {Nhanh hơn Babel 20-70 lần}: single-threaded; even faster with parallelism {đơn luồng; nhanh hơn nữa với song song}
- Drop-in Babel replacement {Thay thế Babel hoàn toàn}: supports most Babel transforms {hỗ trợ hầu hết transform của Babel}
- Used everywhere {Dùng khắp nơi}: Next.js, Vite (plugin-react-swc), Parcel, Deno all use SWC {đều dùng SWC}
- Minification: 7x faster than Terser with comparable output size {nhanh hơn Terser 7 lần với output size tương đương}
Weaknesses {Điểm yếu}
- Not a bundler {Không phải bundler}: doesn’t resolve imports, doesn’t do code splitting {không resolve import, không code splitting}
- Plugin limitations {Hạn chế plugin}: Rust plugin API exists but less accessible than Babel’s JS API {API plugin Rust tồn tại nhưng ít truy cập được hơn API JS của Babel}
- Some Babel plugins missing {Thiếu một số plugin Babel}: very niche transforms may not be available {transform rất đặc thù có thể không có}
When to use {Khi nào dùng}
- You’re already using it (via Vite, Next.js, Parcel) {Bạn đã đang dùng nó}
- Need to replace Babel in a custom pipeline for speed {Cần thay Babel trong pipeline tuỳ chỉnh để nhanh hơn}
- Need fast minification as Terser alternative {Cần minification nhanh thay thế Terser}
// .swcrc — drop-in Babel replacement config
{
"jsc": {
"parser": { "syntax": "typescript", "tsx": true },
"transform": { "react": { "runtime": "automatic" } },
"target": "es2022",
"minify": { "compress": true, "mangle": true }
}
}
Bun (Bundler) {Bun (Bundler)}
Architecture {Kiến trúc}
Bun is a JavaScript runtime (like Node.js) {Bun là JavaScript runtime (như Node.js)} that includes a bundler as part of its toolchain {bao gồm bundler như phần của chuỗi công cụ}. Written in Zig and C++ for maximum performance {Viết bằng Zig và C++ cho performance tối đa}.
Bun Runtime
├── Package manager (bun install) — faster than npm/pnpm
├── Bundler (bun build) — esbuild-class speed
├── Test runner (bun test)
├── Transpiler (native TS/JSX support)
└── Runtime (replaces Node.js)
Strengths {Điểm mạnh}
- All-in-one {Tất cả trong một}: runtime + bundler + package manager + test runner {runtime + bundler + quản lý package + test runner}
- Extremely fast {Cực nhanh}: comparable to esbuild for bundling, faster
node_modulesinstall {tương đương esbuild cho bundle, càinode_modulesnhanh hơn} - Native TypeScript {TypeScript native}: runs
.tsfiles directly without build step {chạy file.tstrực tiếp không cần build} - Node.js compatible {Tương thích Node.js}: most npm packages work {hầu hết package npm hoạt động}
Weaknesses {Điểm yếu}
- Bundler is basic {Bundler cơ bản}: less mature than Vite/Webpack for production apps {ít trưởng thành hơn Vite/Webpack cho app production}
- No HMR/dev server in bundler {Bundler không có HMR/dev server}
- Framework support {Hỗ trợ framework}: React/Vue/Svelte frameworks still prefer Vite {framework React/Vue/Svelte vẫn ưu tiên Vite}
- Compatibility gaps {Khoảng cách tương thích}: some Node.js APIs not yet implemented {một số API Node.js chưa được implement}
- Production maturity {Độ trưởng thành production}: fewer production deployments to learn from {ít deployment production để học từ}
When to use {Khi nào dùng}
- Scripts, CLI tools, backend services {Script, tool CLI, service backend}
- Projects where you want one tool for everything {Project muốn một tool cho mọi thứ}
- Need fastest possible
node_modulesinstall {Cần càinode_modulesnhanh nhất có thể}
When NOT to use {Khi nào KHÔNG dùng}
- Complex frontend apps with framework (use Vite) {App frontend phức tạp với framework}
- Need production-grade code splitting, CSS pipeline {Cần code splitting cấp production, pipeline CSS}
# Bun bundler
bun build ./src/index.tsx --outdir=./dist --minify --sourcemap=external
# Similar to esbuild but with Bun's runtime features
tsup {tsup}
Architecture {Kiến trúc}
tsup is an opinionated library bundler {tsup là bundler thư viện có chủ kiến} that wraps esbuild {bọc esbuild}. It’s designed for one use case {Nó được thiết kế cho một trường hợp sử dụng}: bundling TypeScript libraries with minimal config {bundle thư viện TypeScript với config tối thiểu}.
Strengths {Điểm mạnh}
- Library-focused {Tập trung thư viện}: generates CJS + ESM + types in one command {tạo CJS + ESM + types trong một lệnh}
- Fast {Nhanh}: powered by esbuild {được esbuild cung cấp sức mạnh}
- DTS generation {Tạo DTS}: automatically generates
.d.tsdeclaration files {tự động tạo file khai báo.d.ts} - Zero config {Không cần config}: works with just a
package.jsonentry point {hoạt động chỉ với entry pointpackage.json}
Weaknesses {Điểm yếu}
- Not for apps {Không cho app}: no dev server, no HMR, no HTML handling {không dev server, HMR, xử lý HTML}
- Limited transforms {Transform hạn chế}: inherits esbuild’s limitations {kế thừa hạn chế của esbuild}
- DTS can be slow {DTS có thể chậm}: TypeScript type-checking is still JavaScript-speed {kiểm tra type TypeScript vẫn tốc độ JavaScript}
When to use {Khi nào dùng}
- Publishing TypeScript libraries to npm {Xuất bản thư viện TypeScript lên npm}
- Need dual CJS/ESM output with types {Cần output CJS/ESM đồng thời với types}
// tsup.config.ts
import { defineConfig } from 'tsup';
export default defineConfig({
entry: ['src/index.ts'],
format: ['cjs', 'esm'],
dts: true,
clean: true,
sourcemap: true,
});
unbuild {unbuild}
Architecture {Kiến trúc}
Created by the UnJS team (same people behind Nuxt) {Được tạo bởi team UnJS (cùng team Nuxt)}. unbuild wraps Rollup for bundling and mkdist for stub-based builds {bọc Rollup cho bundling và mkdist cho build dạng stub}.
Strengths {Điểm mạnh}
- Passive build mode {Chế độ build thụ động}: “stub” mode links to source during development, only bundles for publish {chế độ “stub” liên kết với source khi dev, chỉ bundle khi publish}
- Auto-inferred config {Config tự suy luận}: reads
package.jsonexports to determine entry points {đọc exportspackage.jsonđể xác định entry point} - Dual CJS/ESM {CJS/ESM đồng thời}: automatic format detection {phát hiện format tự động}
Weaknesses {Điểm yếu}
- Niche {Chuyên biệt}: designed specifically for universal JS libraries {thiết kế riêng cho thư viện JS universal}
- Less known {Ít được biết}: smaller community than tsup {cộng đồng nhỏ hơn tsup}
- Slower than tsup {Chậm hơn tsup}: uses Rollup (JS) instead of esbuild (Go) {dùng Rollup (JS) thay vì esbuild (Go)}
When to use {Khi nào dùng}
- Building universal JS/TS libraries (CJS + ESM) {Xây thư viện JS/TS universal}
- Want stub-based development workflow {Muốn workflow dev dạng stub}
- UnJS/Nuxt ecosystem packages {Package hệ sinh thái UnJS/Nuxt}
// build.config.ts — entries are often auto-inferred from package.json
import { defineBuildConfig } from 'unbuild';
export default defineBuildConfig({
entries: ['src/index'],
declaration: true, // emit .d.ts
clean: true,
rollup: { emitCJS: true }, // dual CJS + ESM
});
The “stub” mode is the killer feature {Chế độ “stub” là tính năng đắt giá nhất}: unbuild --stub writes a tiny re-export to source instead of bundling {ghi một file re-export nhỏ trỏ về source thay vì bundle}, so library changes are reflected instantly while developing a monorepo {nên thay đổi thư viện phản ánh tức thì khi dev trong monorepo}.
Speed Comparison {So sánh tốc độ}
Benchmark: bundling a medium React app (500 modules, 50K lines) {Benchmark: bundle app React trung bình (500 module, 50K dòng)}:
| Tool | Cold Build {Build lạnh} | Hot Rebuild {Rebuild nóng} | Dev Startup {Khởi động dev} |
|---|---|---|---|
| Webpack 5 | ~18s | ~2s | ~8s |
| Vite 6 (Rolldown) | ~1.2s | ~0.1s | ~300ms |
| Rollup 4 | ~8s | ~3s | N/A (no dev server) |
| esbuild | ~0.4s | ~0.05s | N/A |
| Turbopack | ~1.5s | ~0.08s | ~400ms |
| Parcel 2 | ~3s | ~0.3s | ~1.5s |
| Bun build | ~0.5s | ~0.06s | N/A |
Cold build time, drawn to scale (each █ ≈ 0.5s) {Thời gian build lạnh, vẽ theo tỉ lệ (mỗi █ ≈ 0.5s)}:
Cold build (lower = faster)
esbuild █ 0.4s
Bun █ 0.5s
Vite 6 ██ 1.2s
Turbopack ███ 1.5s
Parcel 2 ██████ 3.0s
Rollup 4 ████████████████ 8.0s
Webpack 5 ████████████████████████████████ 18.0s
└── native (Go/Rust/Zig) ──┘ └── JavaScript ──┘
Hot rebuild — the number you feel hundreds of times a day {Rebuild nóng — con số bạn cảm nhận hàng trăm lần mỗi ngày}:
Hot rebuild (lower = faster)
esbuild ▏ 0.05ms-class (instant)
Bun ▏ ~0.06s
Turbopack ▏ ~0.08s ← incremental, function-level cache
Vite 6 ▏ ~0.1s
Parcel 2 ██ ~0.3s
Webpack 5 ████████████████ ~2s
Rollup 4 ██████████████████████ ~3s (no incremental dev)
Key insight {Nhận thức quan trọng}: native tools (Rust/Go/Zig) are 10-40x faster than JavaScript-based tools {tool native nhanh hơn tool JavaScript 10-40 lần}. This is the primary reason for the migration {Đây là lý do chính cho sự di chuyển}.
Output size also matters {Kích thước output cũng quan trọng}
Speed is not everything {Tốc độ không phải tất cả} — for libraries and ship-to-user bundles, smaller output wins {với thư viện và bundle gửi tới user, output nhỏ hơn thắng}. Same React app, minified + gzipped {Cùng app React, minify + gzip}:
Final bundle size (lower = better)
Rollup 4 ████████████████ ~142 KB ← best scope hoisting
Rolldown ████████████████▏ ~143 KB
Vite 6 ████████████████▏ ~144 KB (Rolldown under the hood)
esbuild ██████████████████ ~158 KB (looser minify/splitting)
Webpack 5 ███████████████████ ~165 KB (runtime overhead)
Rollup-family tools still produce the leanest output {Nhóm Rollup vẫn cho output gọn nhất} thanks to mature scope hoisting and tree-shaking {nhờ scope hoisting và tree-shaking trưởng thành}; esbuild trades a few KB for raw speed {esbuild đánh đổi vài KB lấy tốc độ}.
The Ecosystem Relationships {Mối quan hệ hệ sinh thái}
Understanding which tools use which is crucial {Hiểu tool nào dùng tool nào là rất quan trọng}:
┌─────────────────────────────────────────────────────────────────────────┐
│ Framework Layer │
│ Next.js (Turbopack) │ Nuxt (Vite) │ Astro (Vite) │ SvelteKit (Vite) │
├─────────────────────────────────────────────────────────────────────────┤
│ Bundler Layer │
│ Webpack 5 │ Vite 6 │ Turbopack │ Parcel 2 │
├─────────────────────────────────────────────────────────────────────────┤
│ Engine Layer │
│ Rolldown (Rust) │ esbuild (Go) │ SWC (Rust) │
│ └─ Vite prod │ └─ Vite deps │ └─ Next.js, Vite, Parcel │
│ │ └─ tsup │ └─ Transpile + Minify │
└─────────────────────────────────────────────────────────────────────────┘
- Vite uses esbuild (dep pre-bundling) + Rolldown (production bundling) + SWC (transpilation via plugin)
- Next.js uses Turbopack (bundling) + SWC (transpilation)
- Parcel uses SWC (transpilation) + custom Rust core (bundling)
- tsup uses esbuild (everything)
Decision Flowchart {Sơ đồ quyết định}
What are you building?
{Bạn đang xây gì?}
├── Web application {Ứng dụng web}
│ ├── Using Next.js? → Turbopack (default, no choice needed)
│ ├── Using other framework (React Router, Vue, Svelte, Astro)?
│ │ └── Vite (industry standard)
│ ├── Vanilla JS/TS, want zero config?
│ │ └── Parcel (auto-detects everything)
│ └── Legacy app with Webpack?
│ └── Keep Webpack (migration cost > benefit for now)
│
├── npm library {Thư viện npm}
│ ├── TypeScript, simple output?
│ │ └── tsup (fast, minimal config)
│ ├── Need tree-shaking + multiple formats?
│ │ └── Rollup or Rolldown
│ └── Universal library (UnJS ecosystem)?
│ └── unbuild
│
├── Script / CLI tool {Script / tool CLI}
│ ├── Want all-in-one runtime?
│ │ └── Bun (runtime + bundler + test)
│ └── Just need fast bundling?
│ └── esbuild (one command)
│
└── Custom build pipeline {Pipeline build tuỳ chỉnh}
├── Need transpilation speed?
│ └── SWC (replace Babel)
└── Need everything + existing Webpack ecosystem?
└── Webpack 5 (last resort)
Migration Guide {Hướng dẫn di chuyển}
Webpack → Vite {Webpack sang Vite}
The most common migration in 2026 {Di chuyển phổ biến nhất năm 2026}:
| Webpack concept | Vite equivalent |
|---|---|
webpack.config.js | vite.config.ts |
entry | HTML file with <script type="module"> |
Loaders (babel-loader, css-loader) | Built-in transforms + plugins |
MiniCssExtractPlugin | Built-in CSS handling |
DefinePlugin | define in config |
devServer | Built-in dev server |
HtmlWebpackPlugin | Use HTML file as entry |
resolve.alias | resolve.alias (same concept) |
require.context | import.meta.glob() |
Migration steps {Các bước di chuyển}:
- Install Vite + framework plugin {Cài Vite + plugin framework}
- Create
index.htmlas entry point {Tạoindex.htmllà điểm vào} - Move env variables to
import.meta.env{Chuyển biến env sangimport.meta.env} - Replace
require()withimport{Thayrequire()bằngimport} - Replace Webpack-specific syntax (
require.context→import.meta.glob) {Thay cú pháp riêng Webpack} - Test, fix, iterate {Test, sửa, lặp}
Part 2 — Package Managers {Phần 2 — Package Manager}
A build tool is useless without dependencies, and a package manager is what puts them on disk {Build tool vô dụng nếu không có dependency, và package manager là thứ đặt chúng lên disk}. It does three jobs {Nó làm ba việc}:
- Resolve the dependency graph from
package.json+ a lockfile {Resolve đồ thị dependency từpackage.json+ lockfile} - Fetch packages from a registry (npm registry by default) into a cache {Fetch package từ registry (mặc định npm registry) vào cache}
- Link them into a layout your runtime/bundler can resolve (
node_modulesor Plug’n’Play) {Link chúng thành layout mà runtime/bundler resolve được (node_moduleshoặc Plug’n’Play)}
The whole rivalry between npm, Yarn, pnpm and Bun comes down to how they do step 3 {Toàn bộ cuộc đua giữa npm, Yarn, pnpm và Bun nằm ở cách chúng làm bước 3} — and how fast they do all three {và tốc độ làm cả ba}.
The node_modules problem {Vấn đề node_modules}
Early npm nested dependencies literally {npm thời đầu lồng dependency theo đúng nghĩa đen}, producing infamous Windows path-length crashes {tạo ra lỗi vượt độ dài path khét tiếng trên Windows}. The fix was hoisting {Cách sửa là hoisting}: flatten everything to the top of node_modules {làm phẳng mọi thứ lên đầu node_modules}. That solved depth but created two new problems {Việc đó sửa độ sâu nhưng tạo hai vấn đề mới}:
Flat / hoisted node_modules (npm, Yarn Classic)
{node_modules phẳng / hoisted}
node_modules/
├── react/ ← your direct dep
├── lodash/ ← HOISTED — a transitive dep of something else
└── ... ← thousands of packages, all flat
Problem 1 — Phantom dependencies {Dependency ma}:
your code can `import lodash` even though it is NOT in your
package.json. It works by accident, breaks when the hoist changes.
Problem 2 — Doppelgangers / duplication {Trùng lặp}:
app needs lodash@4, a dep needs lodash@3 → BOTH copied to disk,
sometimes many times across the tree.
pnpm and Yarn Berry were built specifically to kill these two problems {pnpm và Yarn Berry sinh ra để diệt đúng hai vấn đề này}.
Package Manager Overview {Tổng quan Package Manager}
| Manager | Language {Ngôn ngữ} | node_modules strategy {Chiến lược} | Lockfile | Default since {Có từ} |
|---|---|---|---|---|
| npm | JavaScript | Flat / hoisted {Phẳng / hoisted} | package-lock.json | Ships with Node.js {Đi kèm Node.js} |
| Yarn Classic (v1) | JavaScript | Flat / hoisted {Phẳng / hoisted} | yarn.lock | 2016 (maintenance mode {chế độ bảo trì}) |
| Yarn Berry (v2-v4) | JavaScript | Plug’n’Play (no node_modules) or linker {PnP hoặc linker} | yarn.lock + .pnp.cjs | 2020 |
| pnpm | TypeScript + Rust (parts) | Symlinks → global content-addressable store {Symlink → store} | pnpm-lock.yaml | 2017 |
| Bun | Zig + C++ | Hoisted + global hardlink cache {Hoisted + cache hardlink} | bun.lock (text) {văn bản} | 2023 |
Three ways to lay out dependencies {Ba cách bố trí dependency}
1. FLAT / HOISTED (npm, Yarn Classic, Bun)
node_modules/{react, lodash, …} ← one big flat folder, copied per project
✓ simple resolution · ✗ phantom deps · ✗ disk duplication across projects
2. CONTENT-ADDRESSABLE STORE + SYMLINKS (pnpm)
~/.pnpm-store/<hash>/… ← every version stored ONCE, globally
node_modules/.pnpm/… ← real files (hardlinked from store)
node_modules/react → symlink ← only your DIRECT deps are visible
✓ no phantom deps · ✓ near-zero extra disk per project · ✓ fast
3. PLUG'N'PLAY (Yarn Berry)
.pnp.cjs ← a resolver map; NO node_modules at all
.yarn/cache/*.zip ← deps stay zipped on disk
✓ instant installs · ✓ strict · ✗ needs PnP-aware tooling
npm {npm}
Architecture {Kiến trúc}
npm is the default {npm là mặc định} — it ships with Node.js, so it is the baseline every other tool is measured against {nó đi kèm Node.js, nên là chuẩn để mọi tool khác đối chiếu}. It installs a flat, hoisted node_modules and records exact versions in package-lock.json {Nó cài node_modules phẳng, hoisted và ghi version chính xác vào package-lock.json}.
package.json + package-lock.json
│ npm install
▼
resolve graph → fetch to ~/.npm cache → hoist & copy into node_modules/
Strengths {Điểm mạnh}
- Zero setup {Không cần cài đặt}: already on every machine with Node.js {đã có sẵn trên mọi máy có Node.js}
- Maximum compatibility {Tương thích tối đa}: every tool, CI, and tutorial assumes npm {mọi tool, CI, tutorial đều mặc định npm}
npx{npx}: run a package binary without installing it globally {chạy binary của package mà không cài global}- Workspaces {Workspaces}: built-in monorepo support since v7 {hỗ trợ monorepo tích hợp từ v7}
- Caching + offline {Cache + offline}: a shared content cache with
--prefer-offline{cache nội dung dùng chung}
Weaknesses {Điểm yếu}
- Slowest of the four {Chậm nhất trong bốn}: still copies files per project {vẫn copy file theo từng project}
- Phantom dependencies {Dependency ma}: hoisting lets you import undeclared packages {hoisting cho phép import package chưa khai báo}
- Disk duplication {Trùng lặp disk}: 10 projects = 10 full copies of
node_modules{10 project = 10 bản copy đầy đủ} - No strictness {Không nghiêm ngặt}: the loose layout hides dependency mistakes until they break {layout lỏng lẻo giấu lỗi dependency đến khi vỡ}
When to use {Khi nào dùng}
- Default for small/simple projects and quick experiments {Mặc định cho project nhỏ/đơn giản và thử nghiệm nhanh}
- Maximum-compatibility environments (legacy CI, locked toolchains) {Môi trường cần tương thích tối đa}
npm install # install from package-lock.json
npm ci # clean, reproducible install for CI {cài sạch cho CI}
npm install react # add a dependency
npx vite # run a binary without a global install
Yarn (Classic + Berry) {Yarn (Classic + Berry)}
Architecture {Kiến trúc}
Yarn comes in two very different generations {Yarn có hai thế hệ rất khác nhau}. Classic (v1) is a faster, deterministic npm clone with the same flat node_modules {Classic (v1) là bản npm clone nhanh hơn, xác định hơn, cùng node_modules phẳng}. Berry (v2-v4) is a rewrite around Plug’n’Play (PnP) {Berry (v2-v4) là bản viết lại quanh Plug’n’Play (PnP)}: no node_modules at all {không có node_modules luôn} — instead a .pnp.cjs resolver maps every import to a zipped package in .yarn/cache {thay vào đó .pnp.cjs ánh xạ mọi import tới package nén trong .yarn/cache}.
Yarn Classic: yarn.lock → flat node_modules (like npm, but faster)
Yarn Berry: yarn.lock + .pnp.cjs → NO node_modules
imports resolved through PnP map → .yarn/cache/*.zip
Strengths {Điểm mạnh}
- PnP = instant + strict {PnP = tức thì + nghiêm ngặt}: no file copying, and phantom deps are impossible {không copy file, và dependency ma là không thể}
- Zero-installs {Zero-install}: commit
.yarn/cacheand clones run with no install step {commit.yarn/cachethì clone về chạy luôn không cần install} - Plugin system {Hệ thống plugin}: extensible CLI (constraints, version policies) {CLI mở rộng được}
- Great workspaces {Workspaces tốt}: mature monorepo tooling {tooling monorepo trưởng thành}
- Classic still everywhere {Classic vẫn phổ biến}: huge install base, very stable {lượng cài đặt lớn, rất ổn định}
Weaknesses {Điểm yếu}
- PnP compatibility friction {Ma sát tương thích PnP}: tools that scan
node_modulesdirectly may need patches {tool quét trực tiếpnode_modulescó thể cần vá} - Two worlds {Hai thế giới}: Classic and Berry feel like different tools — migration is real work {Classic và Berry như hai tool khác nhau — migrate là việc thật}
- Classic is in maintenance {Classic đã ngừng phát triển}: no new features, the team is on Berry {không thêm tính năng mới}
node-moduleslinker {Linkernode-modules}: many teams disable PnP and lose half the benefit {nhiều team tắt PnP và mất nửa lợi ích}
When to use {Khi nào dùng}
- Berry when you want strictness + zero-installs and your toolchain is PnP-ready {Berry khi muốn nghiêm ngặt + zero-install và toolchain sẵn sàng PnP}
- Classic only to maintain existing v1 repos {Classic chỉ để bảo trì repo v1 cũ}
yarn # install (Classic or Berry)
yarn add react # add a dependency
yarn dlx vite # Berry's npx equivalent {tương đương npx}
yarn workspaces foreach run build # run a script across the monorepo
pnpm {pnpm}
Architecture {Kiến trúc}
pnpm (“performant npm”) keeps a single global content-addressable store {pnpm giữ một store địa chỉ-theo-nội-dung toàn cục duy nhất} where each package version is stored exactly once {nơi mỗi version package được lưu đúng một lần}. Each project’s node_modules is built from hardlinks to that store, plus symlinks that expose only your declared dependencies {node_modules của mỗi project dựng từ hardlink tới store, cộng symlink chỉ phơi bày dependency bạn đã khai báo}.
~/.local/share/pnpm/store/ ← global store: react@19 stored ONCE
│ hardlink (no copy, no extra disk)
▼
project/node_modules/.pnpm/react@19/… ← real files
project/node_modules/react → symlink ───┘ ← only DIRECT deps visible
Strengths {Điểm mạnh}
- Disk efficient {Tiết kiệm disk}: 10 projects share one store — near-constant total size {10 project chia sẻ một store — tổng dung lượng gần như không đổi}
- Strict by default {Nghiêm ngặt mặc định}: symlinked layout makes phantom deps impossible {layout symlink khiến dependency ma là không thể}
- Fast {Nhanh}: hardlinking beats copying; warm installs are very quick {hardlink nhanh hơn copy; install nóng rất nhanh}
- First-class monorepos {Monorepo hạng nhất}:
pnpm-workspace.yaml, filtering,--filtertask running {lọc và chạy task theo--filter} - npm-compatible {Tương thích npm}: same commands, same registry, real
node_modules{cùng lệnh, cùng registry,node_modulesthật}
Weaknesses {Điểm yếu}
- Symlinks surprise some tools {Symlink làm vài tool bất ngờ}: rare bundlers/packagers that don’t follow symlinks need config {vài bundler hiếm không theo symlink cần config}
- Stricter = more upfront fixes {Nghiêm ngặt = sửa nhiều lúc đầu}: migrating reveals previously hidden phantom deps {migrate lộ ra dependency ma từng bị ẩn}
- Less default than npm {Ít mặc định hơn npm}: must be installed/enabled (via Corepack) {phải cài/bật (qua Corepack)}
When to use {Khi nào dùng}
- Most teams in 2026 — the sweet spot of speed, disk, and correctness {Hầu hết team năm 2026 — điểm cân bằng giữa tốc độ, disk và đúng đắn}
- Monorepos of any size {Monorepo mọi kích thước}
- CI where disk and install time are billed {CI nơi disk và thời gian install bị tính tiền}
corepack enable # activate pnpm/yarn shipped with Node {bật pnpm/yarn}
pnpm install # install using the global store
pnpm add react # add a dependency
pnpm -r run build # run "build" in every workspace package
pnpm --filter web dev # target a single workspace {nhắm một workspace}
Bun (Package Manager) {Bun (Package Manager)}
Architecture {Kiến trúc}
Beyond the bundler covered in Part 1, bun install is a standalone package manager {Ngoài bundler đã nói ở Phần 1, bun install là một package manager độc lập} written in Zig {viết bằng Zig}. It is the fastest installer available {Nó là installer nhanh nhất hiện có}, using a global hardlink cache and a binary-then-text lockfile {dùng cache hardlink toàn cục và lockfile}. Since Bun 1.2 the default lockfile is the text bun.lock (the old binary bun.lockb is legacy) {Từ Bun 1.2 lockfile mặc định là bun.lock dạng văn bản (bun.lockb nhị phân cũ là legacy)}.
bun install
→ resolve → global cache (~/.bun/install/cache, hardlinked)
→ hoisted node_modules (npm-compatible layout) → bun.lock (text)
Often 10-25x faster than npm on a cold install.
Strengths {Điểm mạnh}
- Fastest installs, by a lot {Install nhanh nhất, cách biệt lớn}: routinely 10-25x faster than npm cold {thường nhanh hơn npm 10-25 lần khi cold}
- Drop-in {Thay thế trực tiếp}: reads
package.json, produces npm-compatiblenode_modules{đọcpackage.json, tạonode_modulestương thích npm} - All-in-one {Tất cả trong một}: same binary is your runtime, bundler, test runner, and PM {cùng binary là runtime, bundler, test runner và PM}
- Workspaces {Workspaces}: monorepo support built in {hỗ trợ monorepo tích hợp}
bunx{bunx}: fastnpxequivalent {tương đươngnpxnhưng nhanh}
Weaknesses {Điểm yếu}
- Younger ecosystem {Hệ sinh thái non hơn}: fewer years of edge-case hardening than npm/Yarn {ít năm tôi luyện edge-case hơn npm/Yarn}
- Hoisted layout {Layout hoisted}: shares npm’s phantom-dependency looseness (not strict like pnpm) {chia sẻ sự lỏng lẻo dependency-ma của npm (không nghiêm ngặt như pnpm)}
- Best inside Bun’s world {Tốt nhất trong thế giới Bun}: some advantages assume you also use the Bun runtime {vài lợi thế giả định bạn cũng dùng Bun runtime}
- Occasional compatibility gaps {Đôi khi có khoảng cách tương thích}: rare native/postinstall edge cases {vài edge case native/postinstall hiếm}
When to use {Khi nào dùng}
- You want the fastest possible installs in dev and CI {Bạn muốn install nhanh nhất có thể ở dev và CI}
- Projects already using the Bun runtime/bundler {Project đã dùng Bun runtime/bundler}
- Greenfield apps comfortable with a newer tool {App mới sẵn sàng dùng tool mới hơn}
bun install # blazing-fast install → bun.lock
bun add react # add a dependency
bun run build # run a package.json script
bunx vite # npx equivalent {tương đương npx}
Install Speed & Disk {Tốc độ Install & Disk}
Benchmark intent: a medium app (~1,200 dependencies) {Ý nghĩa benchmark: app trung bình (~1.200 dependency)}. Numbers are illustrative orders of magnitude {Con số mang tính bậc độ lớn minh hoạ}, not lab-precise — real results vary by network, disk, and cache state {không phải chính xác phòng lab — kết quả thật thay đổi theo mạng, disk và trạng thái cache}.
Cold install — no cache, no lockfile shortcuts {Install lạnh — không cache, không lockfile tắt}:
Cold install (lower = faster)
Bun ███ ~8s
Yarn Berry ████████ ~20s (PnP, no node_modules copy)
pnpm ██████████ ~25s
Yarn Classic ████████████ ~30s
npm ████████████████ ~40s
Warm install — cache primed, lockfile present (the everyday number) {Install nóng — đã có cache + lockfile (con số hằng ngày)}:
Warm install (lower = faster)
Bun ▏ ~1s
Yarn Berry █ ~2s (zero-installs → can be ~0s)
pnpm ██ ~3s
Yarn Classic ██████ ~6s
npm ██████████ ~10s
Disk usage across 10 projects that share most dependencies {Dung lượng disk qua 10 project chia sẻ phần lớn dependency} — this is where layout strategy dominates {đây là nơi chiến lược layout quyết định}:
| Manager | 1 project | 10 projects {10 project} | Why {Vì sao} |
|---|---|---|---|
| npm | ~1.0x | ~10x | Full copy per project {Copy đầy đủ mỗi project} |
| Yarn Classic | ~1.0x | ~10x | Full copy per project {Copy đầy đủ mỗi project} |
| Bun | ~1.0x | ~1-2x | Global hardlink cache {Cache hardlink toàn cục} |
| pnpm | ~1.0x | ~1-2x | One global store, hardlinked {Một store toàn cục, hardlink} |
| Yarn Berry (PnP) | small {nhỏ} | small {nhỏ} | Zipped cache, no node_modules {Cache nén, không node_modules} |
Takeaway {Điểm rút ra}: Bun wins raw speed {Bun thắng tốc độ thô}; pnpm and Yarn Berry win on disk and correctness {pnpm và Yarn Berry thắng về disk và đúng đắn}; npm wins on ubiquity {npm thắng về phổ biến}.
Lockfiles Compared {So sánh Lockfile}
The lockfile pins the exact resolved tree so installs are reproducible {Lockfile ghim chính xác cây đã resolve để install tái lập được}. Never hand-edit it; commit it {Đừng sửa tay; hãy commit nó}.
| Manager | Lockfile | Format | Notes {Ghi chú} |
|---|---|---|---|
| npm | package-lock.json | JSON | Verbose but universal {Dài dòng nhưng phổ quát} |
| Yarn Classic | yarn.lock | Custom text {Văn bản riêng} | Human-readable {Đọc được} |
| Yarn Berry | yarn.lock (+ .pnp.cjs) | YAML-ish | Pairs with the PnP map {Đi cùng map PnP} |
| pnpm | pnpm-lock.yaml | YAML | Compact, diff-friendly {Gọn, dễ diff} |
| Bun | bun.lock | Text (since 1.2) {Văn bản (từ 1.2)} | Replaces binary bun.lockb {Thay bun.lockb nhị phân} |
Golden rules {Quy tắc vàng}: commit exactly one lockfile {commit đúng một lockfile}; use npm ci / pnpm i --frozen-lockfile / yarn install --immutable in CI for reproducible builds {dùng các lệnh frozen trong CI để build tái lập}; never mix two managers in one repo {đừng trộn hai manager trong một repo}.
Monorepos & Workspaces {Monorepo & Workspaces}
All four support workspaces — multiple packages in one repo sharing a single install {Cả bốn đều hỗ trợ workspaces — nhiều package trong một repo dùng chung một lần install} — but maturity differs {nhưng độ trưởng thành khác nhau}:
| Manager | Workspace config | Filtered tasks {Task lọc} | Verdict {Kết luận} |
|---|---|---|---|
| npm | workspaces in package.json | -w, --workspace | Works, basic {Chạy được, cơ bản} |
| Yarn Berry | workspaces field | workspaces foreach, constraints | Powerful {Mạnh} |
| pnpm | pnpm-workspace.yaml | --filter, -r | Best-in-class {Tốt nhất} |
| Bun | workspaces field | --filter | Fast, newer {Nhanh, mới hơn} |
For monorepos, pnpm is the common default in 2026 {Với monorepo, pnpm là mặc định phổ biến năm 2026} thanks to strict isolation + --filter ergonomics {nhờ cô lập nghiêm ngặt + công thái học --filter}, often paired with Turborepo or Nx for task caching {thường ghép với Turborepo hoặc Nx để cache task}.
Package Manager Decision Flowchart {Sơ đồ quyết định Package Manager}
Which package manager?
{Dùng package manager nào?}
├── Brand-new project, want a safe modern default? {Project mới, muốn mặc định hiện đại an toàn?}
│ └── pnpm (fast, disk-efficient, strict, great monorepos)
│
├── A monorepo / many packages? {Monorepo / nhiều package?}
│ └── pnpm (--filter) ─ optionally + Turborepo/Nx for caching
│
├── Absolute fastest installs, OK with a newer tool? {Install nhanh tuyệt đối, chấp nhận tool mới?}
│ └── Bun (especially if already using the Bun runtime)
│
├── Want strict + zero-installs and your tooling is PnP-ready? {Muốn nghiêm ngặt + zero-install?}
│ └── Yarn Berry (PnP)
│
├── Maximum compatibility / locked CI / tiny project? {Tương thích tối đa / CI khoá / project nhỏ?}
│ └── npm (it's already there)
│
└── Maintaining an existing repo? {Bảo trì repo có sẵn?}
└── Keep its current manager — don't mix lockfiles {Giữ manager hiện tại — đừng trộn lockfile}
Tip {Mẹo}: use Corepack (ships with Node.js) to pin the package manager per project via the
packageManagerfield inpackage.json{dùng Corepack (đi kèm Node.js) để ghim package manager cho từng project qua fieldpackageManagertrongpackage.json} — everyone on the team then uses the exact same version {cả team dùng đúng cùng một version}.
The Future (2026-2027) {Tương lai (2026-2027)}
Trends {Xu hướng}
- JavaScript tooling dies {Tooling JavaScript chết}: all performance-critical tools are now Rust/Go/Zig {tất cả tool quan trọng performance bây giờ là Rust/Go/Zig}
- Bundler convergence {Hội tụ bundler}: Vite (via Rolldown) handles 80%+ of projects {Vite xử lý hơn 80% project}
- Framework-owned bundlers {Bundler thuộc framework}: Next.js (Turbopack), Angular (esbuild), Nuxt/Astro/Svelte (Vite) {mỗi framework sở hữu bundler riêng}
- Zero-config becomes standard {Không config trở thành chuẩn}: no one writes 200-line configs anymore {không ai viết config 200 dòng nữa}
- Dev/prod unification {Đồng nhất dev/prod}: same bundler for both (Rolldown in Vite, Turbopack in Next) {cùng bundler cho cả hai}
- Package managers consolidate {Package manager hội tụ}: npm stays the default-by-default, but pnpm is now the team standard and Bun keeps pushing install speed; Corepack pins the choice per repo {npm vẫn là mặc-định-của-mặc-định, nhưng pnpm đã thành chuẩn của team và Bun tiếp tục đẩy tốc độ install; Corepack ghim lựa chọn cho từng repo}
What to learn {Nên học gì}
If you’re starting today {Nếu bạn bắt đầu hôm nay}:
- Vite — the universal standard {tiêu chuẩn phổ quát}. Know it deeply. {Hiểu sâu.}
- esbuild — for scripts, CLI tools, and understanding how fast tools work {cho script, CLI, và hiểu cách tool nhanh hoạt động}
- Webpack — not to use, but to understand when you encounter legacy projects {không để dùng, mà để hiểu khi gặp project cũ}
Quick Reference Card {Thẻ tham khảo nhanh}
| Scenario {Tình huống} | Choose {Chọn} | Why {Vì sao} |
|---|---|---|
| New React/Vue/Svelte app | Vite | Fast, simple, ecosystem standard {Nhanh, đơn giản, chuẩn hệ sinh thái} |
| New Next.js app | Turbopack | Default, no choice needed {Mặc định} |
| Publish TS library to npm | tsup | Fastest DX for library authors {DX nhanh nhất cho tác giả thư viện} |
| Quick prototype | Parcel | Zero config, instant start {Không config, khởi động tức thì} |
| Large legacy app | Webpack (keep) | Migration cost too high {Chi phí di chuyển quá cao} |
| Full-stack script | Bun | All-in-one, native TS {Tất cả trong một} |
| Replace Babel in pipeline | SWC | 20-70x faster, compatible {Nhanh hơn, tương thích} |
| Custom fast bundling | esbuild | Simplest CLI, Go-speed {CLI đơn giản nhất, tốc độ Go} |
| Universal JS library | unbuild | Stub mode + auto config {Chế độ stub + auto config} |
Package manager pick {Chọn package manager}
| Scenario {Tình huống} | Choose {Chọn} | Why {Vì sao} |
|---|---|---|
| New project, modern default | pnpm | Fast, disk-efficient, strict {Nhanh, tiết kiệm disk, nghiêm ngặt} |
| Monorepo / many packages | pnpm (+ Turborepo) | Best --filter + isolation {--filter + cô lập tốt nhất} |
| Fastest installs | Bun | 10-25x faster cold {Nhanh hơn 10-25 lần khi cold} |
| Strict + zero-installs | Yarn Berry (PnP) | No node_modules, committable cache {Không node_modules, cache commit được} |
| Maximum compatibility | npm | Ships with Node, assumed everywhere {Đi kèm Node, mặc định khắp nơi} |
| Existing repo | Keep current {Giữ hiện tại} | Never mix lockfiles {Đừng trộn lockfile} |