jvinhit//lab

Search posts

Type to search across journal entries.

navigate open esc close

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"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/deps lầ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/exclude chỉ cho trường hợp hiếm bị sót.
  • Dep cũ? rm -rf node_modules/.vite hoặc vite --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.