第 31 题:Tree Shaking 原理与常见失效原因(高频 + 难点 + 面试必考)

好的,继续你的 第 36 题:Tree Shaking 原理与常见失效原因(高频 + 难点 + 面试必考)

我会给你:

  • 深入解释(你用得出去的那种)
  • 面试官追问点
  • 常见错误案例
  • 速记卡片

第 36 题:什么是 Tree Shaking?它是如何工作的?为什么有时会失效?


一、什么是 Tree Shaking?(一句话版)

Tree Shaking = 在打包时通过"静态分析",删除没有被使用的代码,从而减小 bundle 体积。

关键词:

  • 静态分析(static analysis)
  • ESModule(import/export)
  • 未被引用的代码会被"摇掉"

二、Tree Shaking 的核心原理(深入讲解)


1. 依赖于 ES Module 的"静态结构"

ESM:

javascript 复制代码
import { a } from './utils.js';

浏览器和打包器在编译阶段就知道:

  • 依赖哪些模块
  • 从模块中引用什么
  • 哪些导出没有被使用

这叫 静态依赖图(static dependency graph)

⚠️ CommonJS 不行,因为它是动态的:

ini 复制代码
const util = require('./' + name);

这种打包器根本分析不了 → 无法 Tree Shaking。

这就是为什么 Tree Shaking 必须是 ESM


2. 标记未使用的导出(Mark phase)

构建工具(Rollup、Webpack、Vite)会:

  1. 扫描所有模块
  2. 识别所有 export
  3. 找出实际被 import 的部分
  4. 将未使用的 export 标记为 "dead code"(死代码)

3. 死代码消除(Dead Code Elimination)

这通常在 压缩阶段(Terser / esbuild)做。

例子:

javascript 复制代码
export function a() {}
export function b() {}

// 引用了 a,没引用 b

最终产物:

csharp 复制代码
function a() {}

b 会直接被丢掉。


三、Tree Shaking 很容易失效(考点重点)

面试官喜欢问:Tree Shaking 什么时候会失败?

我总结出最常见的 7 大类👇


🚨 Tree Shaking 失效的 7 个经典原因


1. 使用 CommonJS(require)而不是 ESModule

ini 复制代码
const util = require('./utils');

⚠️ 导出是动态的 → 无法静态分析 → 不支持 Tree Shaking。


2. 默认导出(default export)中包含多余内容

css 复制代码
export default {
  a: function() {},
  b: function() {}
};

Tree Shaking 不能拆解对象内部成员 → 即使你只用 a,b 也不会被删。


3. 副作用(side effects)代码

arduino 复制代码
// utils.js
console.log("i have side effect");

即使模块内函数未使用,该模块 依然会保留,因为有副作用。


4. 使用动态属性访问

css 复制代码
export const obj = {
  a: () => {},
  b: () => {}
};

然后:

javascript 复制代码
import { obj } from './utils.js';

obj['a']();

打包器无法确定 obj.b 是否会用到 → 无法 Tree Shake。


5. 代码中使用 "引用可能被外部调用" 的成员

典型例子:class 的方法。

javascript 复制代码
export class User {
  login() {}
  delete() {}
}

即使你只调用 new User().login()
delete() 方法也不会被 Tree Shake。

因为类方法看上去都可能被继承或外部访问。


6. 忘记设置 sideEffects: false

package.json:

json 复制代码
{
  "sideEffects": false
}

告诉打包器:

"我这个包里所有文件都没有副作用,可以大胆删。"

否则,打包器会小心谨慎,不敢删多余模块。


7. 使用了使代码变动态的写法

例如:

javascript 复制代码
import * as utils from './utils';

utils[aDynamicName]();

任何动态访问都会让 Tree Shaking 失效。


四、面试官追问(必备)


❓ Tree Shaking 必须依赖 ESM 吗?为什么?

必须。

因为:

  • ESM 静态结构 → 编译时就知道导入/导出
  • CJS 动态结构 → require 可以接变量 → 无法分析依赖

❓ 为什么 class 方法不能 Tree Shaking?

因为 class 方法属于"可动态访问":

scss 复制代码
user['delete']();

打包器无法判定哪些方法是真的没用。


❓ 什么情况下一个模块一定不会被 Tree Shake?

只要模块有副作用:

arduino 复制代码
import './setup.js';  // 内有副作用

即使内容没用到,也不能删。


五、速记卡片(1 分钟复习版)


🟦 Tree Shaking 速记卡片

✔ 什么是 Tree Shaking?

  • 删除没用到的代码(dead code elimination)
  • 基于静态分析(ESM)

✔ 为什么 CJS 不行?

  • require 是动态的 → 无法静态分析

✔ Tree Shaking 失效 7 大原因

  1. 使用 CommonJS(require)
  2. default export 包含多个成员
  3. 模块有副作用(console、DOM 操作等)
  4. 动态访问属性(obj['a'])
  5. class 方法(可能被外部调用)
  6. package.json 未设置 sideEffects: false
  7. 使用动态 require 或动态 import 名称

如果你能把这些背熟,Tree Shaking 面试 100% 秒杀。


要继续第 37 题吗?

👉 "事件循环(Event Loop)与微任务/宏任务" 的进阶版题目?

相关推荐
共享家95273 小时前
搭建 AI 聊天机器人:”我的人生我做主“
前端·javascript·css·python·pycharm·html·状态模式
Halo_tjn5 小时前
基于封装的专项 知识点
java·前端·python·算法
m0_748229997 小时前
Vue2 vs Vue3:核心差异全解析
前端·javascript·vue.js
C澒7 小时前
前端监控系统的最佳实践
前端·安全·运维开发
xiaoxue..7 小时前
React 手写实现的 KeepAlive 组件
前端·javascript·react.js·面试
hhy_smile8 小时前
Class in Python
java·前端·python
快乐非自愿8 小时前
【面试题】MySQL 的索引类型有哪些?
数据库·mysql·面试
小邓吖8 小时前
自己做了一个工具网站
前端·分布式·后端·中间件·架构·golang
南风知我意9578 小时前
【前端面试2】基础面试(杂项)
前端·面试·职场和发展
LJianK19 小时前
BUG: Uncaught Error: [DecimalError] Invalid argument: .0
前端