Vite · Part 4 — Dependency Pre-bundling
Why Vite pre-bundles node_modules into .vite/deps: collapsing hundreds of module requests into one, converting CommonJS to ESM, and caching it all. Plus optimizeDeps include/exclude and when to clear the cache. With a pre-bundle visualizer.
ESM native khi dev có hai vấn đề với node_modules. Một, nhiều package ship hàng trăm file nhỏ — import một cái sẽ kích hoạt hàng trăm request. Hai, nhiều package vẫn là CommonJS, thứ ESM native của trình duyệt không chạy được. Pre-bundle giải quyết cả hai.
Chạy bước pre-bundle và xem hàng trăm file gộp lại thành vài file cache:
1. Hai vấn đề
Vấn đề 1: bùng nổ request. lodash-es ship ~640 module ESM. Nếu Vite phục vụ thô, import { debounce } from "lodash-es" sẽ tỏa thành hàng trăm request lồng — thác nước tàn bạo cả trên localhost.
Vấn đề 2: CommonJS. Nhiều package xuất bằng module.exports / require(). import của trình duyệt chỉ hiểu ESM. Cần thứ gì đó chuyển CJS → ESM.
2. Cách sửa: pre-bundle một lần
Lần đầu khởi động dev, Vite quét source tìm dependency được import và bundle mỗi cái thành một file ESM tối ưu trong node_modules/.vite/deps/:
node_modules/.vite/deps/
├─ lodash-es.js ← ~640 files → 1
├─ react.js ← CommonJS → ESM
├─ react-dom_client.js
└─ _metadata.json ← hashes for cache validation
Giờ import { debounce } from "lodash-es" là một request tới một file. Đây là vì sao, trong demo Phần 3, react xuất hiện như một request dep đơn.
Vite 8 dùng Rolldown để pre-bundle (phiên bản cũ dùng esbuild). Dù sao đó là một lượt nhanh tốc-độ-native chạy dưới một giây cho đa số app.
3. Được cache cứng
Dep đã pre-bundle nhận cache một năm immutable và một hash trong _metadata.json. Vite bỏ qua bundle lại trừ khi có gì liên quan thay đổi. Cache mất hiệu lực khi:
- dependency trong
package.jsonđổi. - lockfile đổi.
- field liên quan trong
vite.config.tsđổi.
4. optimizeDeps — khi bạn phải can thiệp
Tự phát hiện lo ~99% trường hợp. Hai lối thoát:
export default defineConfig({
optimizeDeps: {
// Vite missed it (e.g. imported only via a dynamic/string path)
include: ["my-lib/submodule"],
// already valid ESM, or breaks when pre-bundled
exclude: ["my-esm-only-lib"],
},
});
- ép một dependency được pre-bundle khi scanner không tìm thấy (hay gặp với import sâu hoặc điều kiện).
- giữ một dependency khỏi pre-bundle (đã ESM sạch, hoặc pre-bundle làm hỏng).
5. Cách sửa “dep cũ” ai cũng học
Thi thoảng dep hành xử kỳ lạ sau khi install hoặc đổi branch — thường là cache pre-bundle cũ. Cách sửa:
# clear the optimize cache and re-bundle
rm -rf node_modules/.vite
# or
vite --force
Đây là kiểu “tắt đi bật lại” của Vite, và an toàn.
6. Một lần reload phụ
Nếu khi duyệt, bạn import một dependency Vite chưa pre-bundle, Vite bundle nó tức thì và reload trang nhanh để phục vụ file tối ưu mới. Bạn sẽ thấy thông báo “new dependencies optimized” trong terminal. Để tránh với dep biết-trước-nhưng-lười, liệt kê trong optimizeDeps.include.
7. Bài tập
1. Vì sao phục vụ lodash-es dạng ESM thô khi dev sẽ chậm, và pre-bundle sửa thế nào?
Lời giải
Nó ship ~640 file → hàng trăm request lồng (thác nước sâu). Pre-bundle gộp thành một file tối ưu = một request.
2. Một dependency là CommonJS. import native của trình duyệt chạy trực tiếp được không? Vite làm gì?
Lời giải
Không — ESM native không chạy CJS. Khi pre-bundle Vite chuyển CJS sang ESM để import chạy.
3. Sau khi đổi branch, một dependency báo lỗi lạ. Cách sửa nhanh và an toàn nhất?
Lời giải
Xóa cache pre-bundle: rm -rf node_modules/.vite hoặc vite --force.
Nâng cao:trong demo, chạy pre-bundle và để ý số request trước/sau, rồi ánh xạ mỗi file output về source thô (bundle ESM vs chuyển CJS→ESM).
Điểm chính
- Vite pre-bundle dependency vào
node_modules/.vite/depslần đầu khởi động. - Nó giải quyết bùng nổ request (nhiều file → một) và CommonJS → ESM.
- Cache được hash và mất hiệu lực khi dependency/lockfile/config đổi.
- Dùng
optimizeDeps.include/excludechỉ cho trường hợp hiếm bị sót. - Dep cũ?
rm -rf node_modules/.vitehoặcvite --force.
Tiếp theo
Phần 5 — Đào sâu HMR: cách Hot Module Replacement thay một module mà không mất state, API import.meta.hot, Fast Refresh của framework, và ranh giới HMR.