第 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)与微任务/宏任务" 的进阶版题目?

相关推荐
福兮说2 小时前
前端下载大文件,点了按钮半天没反应:fetch + blob 的三个问题和三种替代写法
前端·javascript·性能优化·文件下载
GreenTea2 小时前
我把 Agent 的 while 循环拆掉了:一种你可能没想到的 Agent 架构
前端·后端·架构
科技观察哨2 小时前
六足平台选型与纳米定位系统集成:HEB-640六自由度位移台在半导体光刻对准中的参数边界与国产替代评估
前端·人工智能
niucloud-admin3 小时前
JAVA V6 多商户商城 开发文档——web前端
前端
颜进强3 小时前
16 · NestJS LifecycleEvents 生命周期事件:五个钩子、三条边界,和 rag-server 的优雅停机
前端·后端·ai编程
今年下半年4 小时前
【VUE】整合腾讯地图、自定义区域边界、村委名称及资产统计(放大显示资产点位)
前端·javascript·vue.js
郑州光合科技余经理4 小时前
同城电商系统:库存变更怎么同步到订单
java·开发语言·前端·后端·uni-app·php·ai编程
我叫黑大帅5 小时前
Go日志库工程选型与逃逸分析评测报告
后端·面试·go
此时不提桶,更待何时6 小时前
05-05-B-对象存储与文件服务面试与生产事故实战
面试·nosql
前端snow6 小时前
ai agent --- DeepAgents 中间件
前端