Tree Shaking Là Gì? Toàn Bộ Kiến Thức Tối Ưu Bundle JavaScript Cho Web Hiện Đại

tree shaking là gì

Tree shaking (hay “rung cây”) là một thuật ngữ trong phát triển web dùng để chỉ quá trình loại bỏ mã nguồn JavaScript không được sử dụng (dead code) khỏi bundle cuối cùng. Kỹ thuật này giúp giảm kích thước file, tăng tốc độ tải trang và cải thiện hiệu suất ứng dụng. Tree shaking hoạt động dựa trên cấu trúc ES modules (import/export tĩnh), cho phép các bundler như Webpack, Rollup hoặc esbuild phân tích và loại bỏ các export không dùng đến. Khái niệm này lần đầu được giới thiệu bởi Rollup.js, sau đó được các công cụ đóng gói khác áp dụng rộng rãi.

Tree Shaking Hoạt Động Như Thế Nào?

tree shaking là gì - Hình 3

Tree shaking không phải là một tính năng “thông minh” tự động mà dựa vào cấu trúc tĩnh của ES modules. Không giống như CommonJS (require) có thể import động, ES modules yêu cầu khai báo import và export ở cấp độ tệp, giúp bundler xác định chính xác mã nào được dùng, mã nào không.

Cơ Chế Phân Tích Tĩnh (Static Analysis)

Bundler quét toàn bộ cây phụ thuộc từ điểm đầu vào. Mỗi module được kiểm tra: các export nào được import ở module khác, các biến, hàm nào thực sự được gọi. Những export không có bất kỳ tham chiếu nào sẽ được đánh dấu là dead code.

Đánh Dấu Export Không Dùng (Unused Harmony Export)

Webpack sử dụng plugin terser-webpack-plugin (hoặc các minifier khác) để thực hiện bước loại bỏ cuối cùng. Kết quả là các khai báo không dùng sẽ bị xoá khỏi bundle, chỉ giữ lại những phần cần thiết.

Ví Dụ Minh Hoạ Cụ Thể

Giả sử thư viện utils.js export 3 hàm: sum, multiply, divide. Trong app.js chỉ import sum. Sau khi tree shaking, bundle cuối chỉ chứa hàm sum, không có multiply và divide.

// utils.js
export function sum(a, b) { return a + b; }
export function multiply(a, b) { return a  b; }
export function divide(a, b) { return a / b; } // app.js
import { sum } from './utils';
console.log(sum(2, 3));

Lợi Ích Của Tree Shaking Đối Với Hiệu Suất Web

    • Giảm kích thước bundle: Loại bỏ dead code giúp file JS nhẹ hơn, đặc biệt quan trọng với các thư viện lớn như lodash, moment.js.
    • Tốc độ tải trang nhanh hơn: Bundle nhỏ hơn đồng nghĩa với thời gian tải xuống ít hơn, nhất là trên mạng di động hoặc kết nối chậm.
    • Cải thiện thời gian parse và compile: Trình duyệt phải phân tích và biên dịch ít mã hơn, giúp trang tương tác nhanh hơn (Faster Time to Interactive).
    • Giảm dung lượng băng thông: Tiết kiệm chi phí hosting và cải thiện trải nghiệm người dùng ở những khu vực có giới hạn dữ liệu.
    • Tối ưu hoá cho mobile: Thiết bị di động có tài nguyên hạn chế; tree shaking giúp giảm áp lực lên CPU và RAM.

    So Sánh Tree Shaking Với Các Kỹ Thuật Tối Ưu Bundle Khác

    tree shaking là gì - Hình 2
    Kỹ thuật Mục tiêu Cách hoạt động Tree Shaking khác gì?
    Minification Nén mã, xoá khoảng trắng, đổi tên biến Terser, UglifyJS Chỉ làm mã nhỏ hơn, không loại bỏ logic không dùng
    Code Splitting Chia bundle thành nhiều phần nhỏ tải theo nhu cầu Dynamic import, React.lazy Tree shaking loại bỏ mã, code splitting chia mã
    Dead Code Elimination Loại bỏ mã không bao giờ chạy DCE trong trình biên dịch Tree shaking là một dạng DCE trong bundle JS

    Điều Kiện Để Tree Shaking Hoạt Động Hiệu Quả

    Sử Dụng ES Modules (import/export)

    CommonJS với require() không hỗ trợ phân tích tĩnh vì có thể import theo điều kiện. Bundle cần cấu hình để chỉ dùng ES modules. Nếu thư viện bên thứ ba xuất bản cả hai định dạng, ưu tiên phiên bản ES module (field “module” trong package.json).

    Cấu Hình Bundler Chính Xác

    • Webpack: Đặt mode: ‘production’, bật sideEffects: false hoặc liệt kê các file có side effect trong package.json.
    • Rollup: Hỗ trợ tree shaking mặc định, cần plugin như @rollup/plugin-node-resolve.
    • esbuild: Có sẵn tree shaking khi set minify: true.
    • Vite: Dùng Rollup phía sau, tự động bật chế độ production.

    Khai Báo sideEffects Trong package.json

    Bundler cần biết file nào có tác dụng phụ (side effect) như import CSS, polyfill. Nếu toàn bộ dự án không có side effect, đặt “sideEffects”: false. Nếu có, liệt kê cụ thể:

    { "name": "my-library", "sideEffects": [ "./src/polyfill.js", ".css" ]
    }
    

    Thực Hành Tree Shaking Với Webpack

    tree shaking là gì - Hình 1

    Cấu Hình Cơ Bản

    Trong webpack.config.js, đảm bảo mode là ‘production’ vì tree shaking chỉ hoạt động ở chế độ này. Sử dụng module.rules với test: /.m?js$/ và exclude: /node_modules/.

    Tối Ưu Thư Viện Bên Thứ Ba

    Với lodash, thay vì import toàn bộ, chỉ import hàm cần: import debounce from ‘lodash/debounce’. Hoặc dùng lodash-es phiên bản ES module hỗ trợ tree shaking. Tương tự, moment.js có thể thay bằng date-fns hoặc dayjs nhỏ gọn hơn.

    Kiểm Tra Kết Quả Tree Shaking

    Dùng Webpack Bundle Analyzer để xem bundle có còn mã chết không. Công cụ này hiển thị thành phần trong bundle dưới dạng biểu đồ, giúp phát hiện các module không cần thiết.

    Hạn Chế Và Những Điều Cần Lưu Ý

    • Side effects: Một số import có tác dụng phụ (ví dụ: import ‘core-js’) không thể bị loại bỏ dù không dùng trực tiếp. Cần khai báo đúng sideEffects.
    • Dynamic import: Tree shaking không hoạt động với các import động qua đường dẫn biến.
    • Thư viện không hỗ trợ ES modules: Một số thư viện cũ chỉ xuất bản CommonJS, không thể tree shake. Cần tìm bản thay thế hoặc wrap.
    • Object destructuring: Import { a, b } from ‘lib’ có thể không được tối ưu nếu thư viện dùng default export hoặc có cấu trúc phức tạp.
    • Export tái xuất (Re-export): Nếu một module re-export tất cả từ module khác, có thể gây khó khăn cho bundler. Nên re-export chọn lọc.

    Sai Lầm Thường Gặp Khi Áp Dụng Tree Shaking

    • Quên khai báo sideEffects trong package.json: Dẫn đến bundler loại bỏ nhầm các file cần thiết như CSS, polyfill.
    • Import toàn bộ thay vì import chọn lọc: Ví dụ import * as _ from ‘lodash’ thay vì import { debounce } from ‘lodash-es’.
    • Để chế độ development: Webpack chỉ thực hiện tree shaking ở production mode. Lưu ý khi deploy.
    • Sử dụng transpiler không tương thích: Babel có thể chuyển đổi ES modules thành CommonJS nếu không cấu hình đúng. Thêm “@babel/preset-env” với modules: false để giữ nguyên ES module.
    • Không kiểm tra bundle output: Nhiều dev nghĩ rằng tree shaking tự động xử lý mọi thứ, nhưng cần verify bằng phân tích thực tế.

    Tree Shaking Trong Các Framework Frontend

    React

    Create React App (CRA) đã tích hợp Webpack với tree shaking. Khi import component từ thư viện, chỉ phần dùng mới được bundle. Cần tránh import kiểu import ‘antd/dist/antd.css’ toàn bộ, thay vào đó dùng import CSS component riêng.

    Vue.js

    Vue CLI cũng hỗ trợ tree shaking trong production. Với Vue 3, Composition API và các helper không dùng sẽ tự động bị loại bỏ nhờ cấu trúc ES module.

    Angular

    Angular CLI sử dụng Webpack và Tree Shaking có sẵn với –prod flag. Tuy nhiên do Angular dùng TypeScript và decorators, quá trình này phức tạp hơn, cần Ivy compiler để tối ưu.

    Lưu Ý Quan Trọng Khi Tối Ưu Bundle Bằng Tree Shaking

    • Luôn kiểm tra bundle ở chế độ production trước khi deploy.
    • Sử dụng các công cụ phân tích như webpack-bundle-analyzer, Rollup Plugin Visualizer.
    • Ưu tiên các thư viện nhỏ, hiện đại hỗ trợ ES modules (lodash-es, date-fns, immer, zustand thay vì Redux cũ).
    • Cấu hình Babel để giữ nguyên ES modules: “modules”: false trong presets.
    • Với monorepo hoặc workspace, đảm bảo sideEffects được khai báo ở cấp package.
    • Đối với thư viện tự phát triển, viết code theo dạng ES module và xuất bản cả định dạng ESM lẫn CJS nhưng luôn ưu tiên ESM.

Câu Hỏi Thường Gặp Về Tree Shaking

Tree shaking có hoạt động với CSS không?

Tree shaking nguyên bản chỉ áp dụng cho JavaScript. Với CSS, có các công cụ như PurgeCSS để loại bỏ class không dùng, nhưng đây là kỹ thuật khác (dead CSS elimination). Một số bundler có plugin kết hợp cả hai.

Tại sao tree shaking không loại bỏ được hết dead code?

Nguyên nhân thường do side effects không được khai báo đúng, module dùng CommonJS, hoặc có mã gây ảnh hưởng phụ (ví dụ: thêm prototype vào global). Cần kiểm tra từng nguyên nhân.

Có nên luôn đặt “sideEffects”: false cho mọi package không?

Không. Nếu package có import các file CSS, polyfill, hoặc thay đổi global scope, việc đặt false sẽ làm mất các chức năng đó. Chỉ đặt false khi chắc chắn không có side effect.

Webpack tree shaking có giống Rollup không?

Giống về nguyên tắc, nhưng Rollup được đánh giá tốt hơn trong việc tree shake các thư viện nhờ cơ chế phân tích tĩnh mạnh mẽ. Webpack phụ thuộc nhiều vào plugin terser để loại bỏ cuối cùng.

Làm cách nào để biết tree shaking đang hoạt động?

Chạy lệnh build production, sau đó dùng Bundle Analyzer hoặc kiểm tra file đầu ra trong thư mục dist. Nếu thấy các hàm không import nhưng vẫn nằm trong bundle, tree shaking chưa hiệu quả.

Kết Luận

Tree shaking là kỹ thuật không thể thiếu trong phát triển web hiện đại, giúp tối ưu bundle JavaScript bằng cách loại bỏ mã không dùng. Để áp dụng hiệu quả, cần hiểu rõ cơ chế ES modules, cấu hình đúng bundler và thư viện, đồng thời kiểm tra thường xuyên bằng công cụ phân tích. Kết hợp tree shaking với code splitting và minification sẽ mang lại hiệu suất tối đa cho ứng dụng. Đầu tư thời gian vào việc tối ưu bundle không chỉ cải thiện trải nghiệm người dùng mà còn giảm chi phí băng thông và tăng điểm Lighthouse, Core Web Vitals.

Bài viết cùng chủ đề:

Để lại một bình luận

Email của bạn sẽ không được hiển thị công khai. Các trường bắt buộc được đánh dấu *