KIếN THứC WEBSITE › PERFORMANCE

Tree shaking là gì — loại code không dùng trong bundle JS

Tree shaking là gì — loại code không dùng trong bundle JS 2026

Tree shaking tự động loại bỏ code không dùng khỏi bundle cuối — giảm mạnh kích thước JS khi dự án import thư viện lớn. Hiểu đúng điều kiện để tree shaking hoạt động sẽ tránh được lỗi bundle vẫn nặng dù đã bật.

Tree shaking là gì

Tree shaking là gì
Tree shaking là gì

Tree shaking là phân tích tĩnh của công cụ build — duyệt cây import/export, xác định code không được gọi và loại bỏ khỏi bundle. Tên gọi xuất phát từ hình ảnh rung cây để lá khô rớt xuống.

Khái niệm này xuất hiện từ Rollup năm 2015, sau đó Webpack 2 áp dụng. Hầu hết công cụ build hiện đại (Vite, esbuild, Rollup, Webpack 5) đều hỗ trợ tree shaking mặc định trong chế độ production.

Trước và sau tree shaking — ví dụ thực tế

  • Trước: Import 1 hàm từ lodash → bundle kéo theo toàn bộ lodash ~70KB.
  • Sau tree shaking: Bundle chỉ chứa hàm đó và các phụ thuộc của nó → 5–10KB.
  • Kết hợp tách bundle: Chia bundle theo route → mỗi trang chỉ tải đúng phần cần, xuống còn 2–5KB mỗi route.

Tree shaking liên quan tới INP và LCP thế nào

JS bundle nặng là nguyên nhân chính khiến INP kém — trình duyệt parse và thực thi JS block luồng chính. Tree shaking giảm lượng JS phải xử lý ngay khi tải trang, cải thiện INP và giảm TBT.

Đọc thêm về INP — chỉ số tương tác thay thế FIDtách bundle theo route để hiểu bức tranh đầy đủ hơn.

Cơ chế hoạt động

Cơ chế hoạt động
Cơ chế hoạt động

Tree shaking yêu cầu 3 điều kiện kỹ thuật. Thiếu bất kỳ điều kiện nào, công cụ build sẽ giữ nguyên toàn bộ module để an toàn.

3 điều kiện bắt buộc

  • ES module (import/export): CommonJS (require/module.exports) không phân tích được ở thời điểm build vì dynamic. Thư viện phải có phiên bản ES module (trường "module" trong package.json).
  • Import tĩnh: import X from 'lib' hoạt động. require(`lib-${var}`) không hoạt động vì công cụ build không biết trước giá trị biến.
  • sideEffects: false: package.json khai báo module không có tác động phụ. Công cụ build mới dám xóa code mạnh hơn.

Quy trình build phân tích

// Bước 1: Parse cây import
import { debounce } from 'lodash-es';
import { format } from 'date-fns';

// Bước 2: Đánh dấu export được dùng
debounce(...)  // đánh dấu "debounce" cần giữ
format(...)    // đánh dấu "format" cần giữ

// Bước 3: Tree shaking
// lodash-es: chỉ giữ "debounce" + phụ thuộc của nó
// date-fns: chỉ giữ "format" + locale dùng

// Bước 4: Xuất bundle
// Kết quả: ~8KB thay vì ~200KB

Thư viện hỗ trợ tree shaking

Thư viện hỗ trợ tree shaking
Thư viện hỗ trợ tree shaking

Không phải thư viện nào cũng tree-shake được. Kiểm tra 3 trường trong package.json: module (entry ES module), sideEffectstype.

Thư viện tree shaking tốt

  • lodash-es: Phiên bản ES module của lodash — tree-shake từng hàm riêng lẻ.
  • date-fns: Mỗi hàm là một file riêng, import theo tên cụ thể.
  • react / react-dom: ES module hiện đại, tree shaking hoạt động tốt.
  • Material UI v5+: Khai báo sideEffects, import theo tên tree-shake được.
  • Tailwind CSS (JIT): Tự loại class không dùng trong quá trình build — bản chất là tree shaking CSS.

Thư viện không tree-shake được

  • Moment.js: CommonJS, kéo theo toàn bộ locale. Thay bằng date-fns.
  • jQuery: CommonJS, nguyên khối. Plugin jQuery yêu cầu toàn bộ thư viện.
  • Bootstrap 4 trở xuống: Bundle SCSS đầy đủ, không tree-shake. Bootstrap 5 đã hỗ trợ ES module.

Lỗi thường gặp khiến tree shaking thất bại

Dự án tưởng đã tree-shake nhưng bundle vẫn nặng — thường là một trong bốn lỗi phổ biến dưới đây.

4 lỗi cần kiểm tra ngay

  • Wildcard import: import * as _ from 'lodash' kéo toàn bộ thư viện dù chỉ dùng 1 hàm. Dùng named import: import { debounce } from 'lodash-es'.
  • sideEffects khai báo sai: Khai báo false nhưng module có CSS tự inject → CSS bị xóa = giao diện vỡ. Đúng cú pháp: "sideEffects": ["*.css", "*.scss"].
  • Babel chuyển ES module sang CommonJS: Cấu hình babel-preset-env target IE11 chuyển import thành require — mất toàn bộ tree shaking. Đặt targets về es2020 trở lên.
  • Re-export wildcard trong index.js: export * from './foo' đôi khi chặn tree shaking. Dùng named re-export: export { X } from './foo'.

Kiểm tra bundle sau tree shaking

Công cụ phân tích bundle

  • webpack-bundle-analyzer: Hiển thị bản đồ treemap từng module, dễ thấy module nào nặng bất thường.
  • rollup-plugin-visualizer: Tương tự cho Rollup và Vite.
  • source-map-explorer: Phân tích source map, xác định chính xác file nào chiếm bao nhiêu KB.

Quy trình audit bundle

  • Chạy build production: npm run build
  • Mở công cụ phân tích, xem module nào chiếm tỉ lệ lớn bất thường.
  • Tìm module CommonJS hoặc wildcard import là nguyên nhân chính.
  • Thay thế thư viện hoặc đổi cách import, build lại và so sánh.

Tree shaking trong ngữ cảnh WordPress

Trang WordPress truyền thống (theme + plugin) không có pipeline build — tree shaking không áp dụng trực tiếp. Plugin enqueue file JS đầy đủ mỗi trang.

Khi nào WordPress có thể dùng tree shaking

  • Theme Gutenberg block tùy biến: Block được xây dựng bằng React + Webpack — tree shaking áp dụng cho JS của block.
  • WordPress headless + Next.js: Frontend có pipeline build đầy đủ — tree shaking hoạt động hoàn toàn. Đọc thêm về tách bundle theo route cho Next.js.
  • Theme tùy biến với Vite/Webpack: Asset được build qua bundler — tree shaking áp dụng cho JS của theme.

Giải pháp tương đương cho WordPress truyền thống

  • Plugin Asset CleanUp hoặc Perfmatters: bỏ script không dùng theo từng trang — tương tự tree shaking ở mức thô hơn.
  • Dequeue script cụ thể qua wp_dequeue_script() trong functions.php.
  • Kết hợp với defer và async để giảm ảnh hưởng của JS lên thời gian hiển thị.

Checklist triển khai tree shaking

  1. Kiểm tra thư viện có phiên bản ES module không — tìm trường "module" trong package.json.
  2. Thay import * as X bằng named import cho mọi thư viện.
  3. Xác nhận không có cấu hình Babel chuyển ES module sang CommonJS.
  4. Kiểm tra sideEffects trong package.json của thư viện — đặc biệt thư viện có CSS.
  5. Chạy npm run build chế độ production, mở công cụ phân tích bundle.
  6. Xác định và thay thế thư viện CommonJS bằng thư viện ES module tương đương.
  7. Kết hợp với preloaddefer/async để tối ưu toàn bộ vòng đời tải JS.

Bài liên quan

Câu hỏi thường gặp

Tree shaking khác minify thế nào?

Khác về bản chất. Minify xóa khoảng trắng và đổi tên biến, nhưng toàn bộ code vẫn thực thi.

Tree shaking loại bỏ hoàn toàn code không bao giờ được gọi.

Hai kỹ thuật bổ sung cho nhau, không thay thế nhau.

Vite có tree shaking mặc định không?

Có. Vite production build (vite build) dùng Rollup với tree shaking mạnh.

Chế độ dev bỏ qua để HMR nhanh hơn.

Bundle production tự động tree-shake mọi import.

Bundle vẫn lớn dù đã bật tree shaking — nguyên nhân ở đâu?

Kiểm tra theo thứ tự sau.

  • Thư viện CommonJS: Moment.js, jQuery — không tree-shake được.
  • sideEffects chưa khai báo: Công cụ build không dám xóa mạnh.
  • Wildcard import: import * kéo toàn bộ module.
  • Babel target sai: Transpile ES module sang CommonJS.

Dùng webpack-bundle-analyzer hoặc rollup-plugin-visualizer để xem module nào chiếm phần lớn bundle.

WordPress có tree shaking CSS không?

Có 3 cách: Tailwind CSS JIT tự loại class không dùng; PurgeCSS quét HTML/JS và loại class không xuất hiện; Asset CleanUp bỏ toàn file CSS không cần theo từng trang.

Tailwind JIT và PurgeCSS chính xác hơn. Asset CleanUp là cách thô nhưng hiệu quả cho WordPress truyền thống.

Web22 audit bundle, xác định module nào gây nặng và đề xuất thay thế thư viện phù hợp. Xem dịch vụ tối ưu Core Web Vitals để tư vấn chi tiết.