jvinhit//lab

Search posts

Type to search across journal entries.

navigate open esc close

Webpack · Part 7 — Tree Shaking & Production Mode

How Webpack drops unused exports: why ES modules are required, the role of sideEffects and usedExports, minification with Terser, and what mode: production turns on. With an interactive tree-shaking visualizer.

Bạn import hai hàm từ một file util xuất hai mươi. Tree shaking là cách Webpack đảm bảo mười tám hàm còn lại không bao giờ tới bundle. Nhưng nó chỉ chạy dưới điều kiện cụ thể, và làm sai sẽ âm thầm ship code chết.

Bật/tắt các điều kiện bên dưới và xem export nào sống sót:


1. Tree shaking là gì

Tree shaking là loại bỏ code chết cho export của ES module. Webpack phân tích export nào thực sự được import xuyên đồ thị, đánh dấu cái không dùng, và minimizer xóa chúng. “Cây” là đồ thị phụ thuộc; bạn “rung” lá chết rụng.


2. Nó cần ES module

Đây là điều kiện không thương lượng. Tree shaking dựa vào cấu trúc tĩnh của import/export — Webpack thấy lúc build chính xác cái gì được dùng:

// ✓ ESM — statically analyzable
import { add } from "./utils";

// ✗ CommonJS — dynamic, can't be shaken
const { add } = require("./utils");

require() là lời gọi hàm có thể có điều kiện hoặc tính toán, nên Webpack không thể an toàn biết cái gì không dùng. Chuyển demo sang CommonJS và xem mọi export sống sót. Quan trọng, đừng để Babel transpile ESM của bạn thành CommonJS — đặt @babel/preset-env thành modules: false.


3. Nó cần production mode

development, Webpack đánh dấu export không dùng nhưng không xóa (để build nhanh và gỡ lỗi được). Việc xóa thực sự xảy ra khi minify, chỉ chạy ở production:

module.exports = {
  mode: "production", // enables usedExports + minimize
};

Chuyển demo sang development mode để thấy code chết ở lại bundle.


4. sideEffects — bỏ cả module

Một số file làm việc chỉ bằng việc được import — một CSS import, một polyfill, một đăng ký global. Webpack không thể bỏ chúng dù export không dùng, vì xóa sẽ đổi hành vi. Bạn báo Webpack file nào thuần qua package.json:

{
  "sideEffects": false
}

false nghĩa là “mọi module ở đây không có side-effect, bỏ cái nào export không dùng”. Nếu vài file side-effect, liệt kê chúng:

{ "sideEffects": ["*.css", "./src/polyfills.js"] }

Đây là cờ tác động lớn nhất để thu nhỏ bundle khi dùng thư viện.


5. Minify với Terser

Ở production, Webpack chạy TerserPlugin để minify JS — rút tên, bỏ khoảng trắng, bỏ nhánh chết. Nó bật mặc định; bạn chỉ cấu hình để tùy chỉnh:

const TerserPlugin = require("terser-webpack-plugin");

optimization: {
  minimize: true,
  minimizer: [
    new TerserPlugin({ terserOptions: { compress: { drop_console: true } } }),
    "...", // keep the defaults (e.g. CSS minimizer) too
  ],
}

Kết hợp với DefinePlugin đặt NODE_ENV thành "production" (Phần 4), Terser có thể loại bỏ cả khối if (process.env.NODE_ENV !== "production") chỉ-dành-cho-dev.


6. Viết code tree-shake được

  • Dùng named ESM export, không phải một object default khổng lồ.
  • Chỉ import cái cầnimport { debounce } from "lodash-es", không phải import _ from "lodash".
  • Ưu tiên thư viện ship ESM và khai báo sideEffects (vd lodash-es hơn lodash).
  • Tránh side-effect ở cấp cao nhất trong module muốn được tree-shake.

7. Bài tập

1. Bạn import một hàm từ file utils nhưng cả file vào bundle. Bạn dùng require(). Vì sao nó phá tree shaking?

Lời giải

CommonJS động, không phân tích tĩnh được — Webpack không chứng minh được cái gì không dùng. Dùng ESM import.

2. Tree shaking chạy ở bản prod nhưng bản dev vẫn chứa export không dùng. Có phải lỗi không?

Lời giải

Không — code chết chỉ bị xóa ở production bởi minimizer; dev chỉ đánh dấu.

3. Helper không dùng của thư viện không rung ra dù ESM + production. package.json của thư viện nên khai báo gì?

Lời giải

"sideEffects": false (hoặc danh sách file có side-effect) để Webpack an toàn bỏ module thuần.

Nâng cao:trong trình trực quan, đặt ESM + production + sideEffects, để ý kích thước bundle, rồi đổi sang CommonJS và xem mọi export không dùng quay lại.


Điểm chính

  • Tree shaking = loại bỏ code chết cho export ESM không dùng.
  • cần ES modulerequire() không rung được.
  • chỉ xóa code ở production.
  • "sideEffects": false cho Webpack bỏ cả module thuần.
  • TerserPlugin minify và bỏ nhánh chết ở production.

Tiếp theo

Phần 8 — Caching & caching dài hạn: [contenthash], moduleIds tất định, tách runtime, và chiến lược đặt tên file cho phép trình duyệt cache bundle cả năm.