BridgeCraft logobridgecraft
週報顧問服務作品集關於聯絡登入
聯絡

bridgecraft

從資料視覺化到 AI Agent,一直在找下一個 meta

文章主題指南週報顧問服務Labs作品集系統架構關於聯絡RSS

© 2026 BridgeCraft — Waiting7777. All rights reserved.

← 所有文章

Tree-Shaking

2021年5月15日 · Waiting7777 · 4 分鐘閱讀

Rich Harris 和他的 Rollup

知名的 rollup.js 的作者 Rich Harris 在 2015 年 12 月的一篇 blog [Tree-shaking versus dead code elimination] 中首次提到了 Tree-Shaking 的概念。

I’ve been working (albeit sporadically of late, admittedly) on a tool called Rollup, which bundles together JavaScript modules. One of its features is tree-shaking, by which I mean that it only includes the bits of code your bundle actually needs to run.

Rich Harris 在文中提到 Tree-Shaking 是為了 Dead code elimination ,這是常見的 compiler optimization 優化方式,簡單來說就是消除無用的 code。那麼什麼是 Dead code 呢?

Dead code

Dead code 包含以下:

  • unreachable code - 永遠不會被執行到的 code
  • dead variables - 宣告了但永遠不會被讀取的變數

舉例來說,以下這段 return 之後的 code 就永遠不會被執行到,然後 b 宣告了卻沒人用它。

function foo() {
    const a = 24;
    const b = 25; // dead variables
    const c = a * 4;
    return c;
    b = 24; // unreachable code
    return 0;
}

需要注意的是,如果 module 宣告了未被使用,也可以看做 Dead code ,比如下面的 bar module。

// foo.js
function foo() {
    console.log('foo');
}
export default foo;

// bar.js
function bar() {
    console.log('bar');
}
export default bar;

// index.js
import foo from './foo.js';
import bar from './bar.js';
foo()

// 雖然有引入bar,但並未使用

Dead code 我們知道了,那麼 Tree-Shaking 呢?

在傳統的靜態語言中,complier 可以判斷出某些程式根本不影響輸出,所以我們可以借助 compiler 來幫忙把 Dead code 刪除。但 JavaScript 是動態語言,compiler 無法幫我們完成,所以得自己實現 Dead code elimination 。

我們平常說的 Tree-Shaking 就是 dce 的一種實現,借助於 ES Module,來消除無用的 module。

ECMA Script 6 module

JavaScript 的模組化經歷過一個漫長的發展過程,在最一開始 JavaScript 是沒有模組化概念的,我們只能借助閉包來讓,後來社群出現了以 RequireJS 為代表的 AMD 規範,和以 Sea.js 為代表的 CMD 規範,Nodejs server 端也出現了 CommonJS 規範,再來 ES6 之後原生引入了 ES Module,取代了社群方案成為了瀏覽器端統一的解決方案。

  • ES Module 輸出的是值的引用,而 CommonJS 輸出的是複製
  • ES Module 是編譯時執行,而 CommonJS 則是在運行時載入

所以 ES Module 最大的特點就是靜態化,在編譯時就能確定模組的依賴關係,以及輸入輸出的值。這也代表了依賴關係是確定的,所以就讓 Tree-Shaking 成為可能。這也是為什麼 rollup 和 webpack 都要用 ES Module 才能支援 Tree-Shaking

Tree-Shaking

藉由 ES Module 的靜態結構,透過編譯階段的靜態分析,找到沒有引入的模組並做標記,到了壓縮階段利用像 uglift-js 這樣的壓縮工具刪除這些沒用到的模組。

以 webpack 為例,驗證一下:

新建資料夾並且初始化檔案,並安裝最新的 webpack

$ mkdir tree-shaking && cd tree-shaking
$ npm init -y
$ npm i webpack webpack-cli -D

新增個 webpack config以及入口 index.js 以及 math.js 模組

// webpack.config.js
const path = require("path");

module.exports = {
  mode: "development",
  entry: "./src/index.js",
  output: {
    filename: "bundle.js",
    path: path.resolve(__dirname, "dist"),
  },
  optimization: {
    // 开启 usedExports  收集 Dead code 相关的信息
    usedExports: true,
  },
};

// src/math.js
export function square(x) {
  return x * x;
}

export function cube(x) {
  var a, b, c; // 这里引入了三个未使用的变量作为 Dead code 的一种
  return x * x * x;
}

// src/index.js
import { cube } from "./math.js";

function component() {
  var element = document.createElement("pre");
  element.innerHTML = "5 cubed is equal to " + cube(5);
  return element;
}

document.body.appendChild(component());

執行 webpack 打包指令,然後觀察包出來的 bundle.js 中 math.js 的 code 。

/***/ "./src/math.js":
/*!*********************!*\
  !*** ./src/math.js ***!
  \*********************/
/***/ ((__unused_webpack_module, __webpack_exports__, __webpack_require__) => {

eval("/* harmony export */ __webpack_require__.d(__webpack_exports__, {\n/* harmony export */   \"cube\": () => (/* binding */ cube)\n/* harmony export */ });\n/* unused harmony export square */\nfunction square(x) {\r\n  return x * x;\r\n}\r\n\r\nfunction cube(x) {\r\n  var a, b, c;\r\n  return x * x * x;\r\n}\r\n\n\n//# sourceURL=webpack://tree-shaking/./src/math.js?");

/***/ })

將 eval 裡面的內容提取出來比較好閱讀:

/* harmony export */
__webpack_require__.d(__webpack_exports__, {
  /* harmony export */
  cube: () => /* binding */ cube /* harmony export */,
});
/* unused harmony export square */
function square(x) {
  return x * x;
}
function cube(x) {
  var a, b, c;
  return x * x * x;
}

可以發現, webpack_exports 只導出了 cube 函數,沒用到的 square 並沒有被導出,並且多了 /* unused harmony exprot square */ 的註解,但 cube 裏面沒用到的 a, b, c 還是被打包了。從這邊就可以理解 webpack 可以通過 Tree-Shaking 找出未使用的模組,但並不會刪除 Dead code。

接著將 mode 切換到 production 已啟用 uglify-js 進行壓縮,然後再次執行打包指令。

(() => {
  "use strict";
  var e, t;
  document.body.appendChild(
    (((t = document.createElement("pre")).innerHTML =
      "5 cubed is equal to " + (e = 5) * e * e),
    t)
  );
})();

結果如當初預期一樣, uglify-js 在壓縮的同時刪除了 Dead code :

  • 未使用的 square 函數
  • 未使用的 a, b, c

結論

從 ES Module 的角度來理解 Tree-Shaking,因為 JavaScript 是動態語言,原本的模組載入方式打包工具無法判斷 code 裡面是否有用到模組,所以只能全部打包起來,而改用 ES Module 之後,打包工具就能在編譯時判斷並找出未使用的模組,並將其 Tree-Shaking。而也經由實驗得知,將 Dead code 移除是 uglify-js 做的而不是打包工具。

每週一篇 — 技術趨勢背後的商業邏輯

分享:

Waiting7777

WoW Arena 冠軍轉前端,用電競 meta 思維拆解技術趨勢。

關於作者

相關文章

AI 時代悲劇:爬蟲吃光內容 最後誰來餵 AI?AI

AI 時代悲劇:爬蟲吃光內容 最後誰來餵 AI?

從遊戲設計角度看這個問題:一個讓所有玩家都想當 free rider 的機制設計,最後一定崩潰。分析 AI 公司、內容創作者、讀者三方的利益衝突,以及可能的出路長什麼樣。

2026年8月14日 · HW SHU · 7 分鐘閱讀

繼續閱讀 →
YC 裡越來越多二次創業者 — AI 時代為什麼老手反而更吃香創業創投+4

YC 裡越來越多二次創業者 — AI 時代為什麼老手反而更吃香

為什麼 AI 時代反而讓「老手」更吃香?從 YC 的數據看 repeat founder 的優勢在哪,以及這對第一次創業的人有什麼啟示。

2026年8月12日 · Waiting7777 · 7 分鐘閱讀

繼續閱讀 →
OpenChamber:專為 AI Agent 設計的開發環境,跟 IDE 的差別在哪?AI Agents開發工具+5

OpenChamber:專為 AI Agent 設計的開發環境,跟 IDE 的差別在哪?

拆解 OpenChamber 的設計邏輯:為什麼 agent 需要專屬的開發環境、跟直接在 terminal 跑 Claude Code 有什麼本質差異,以及這個方向的商業潛力在哪。

2026年8月11日 · Waiting7777 · 8 分鐘閱讀

繼續閱讀 →

這篇文章對你有幫助嗎?

每週一篇 — 技術趨勢背後的商業邏輯

AI 產業在變什麼、工程師該注意什麼——拆清楚寄到你的信箱。