TreeShaking 与 SideEffect

Rollup 有很强大的程序流分析,从而可以大量移除冗余的、未被引用的代码,但是一些情况下 Rollup 为了保守起见,依然会保留这些代码,这些情况包括:

  1. import 的模块可能存在 side effect
  2. 引用的对象可能存在 side effect

参考以下的 FAQ tree-shaking-doesnt-seem-to-be-working

Example

最近我在实际的业务中就遇到了类似的情况,例如有 module-a.jsmodule-b.js 模块:

javascript 复制代码
// module-b.js
var EnumType
(function (EnumType) {
	EnumType['A'] = 'A';
})(EnumType)

export {
	EnumType
}
javascript 复制代码
// module-a.js
import { EnumType } from './module-b'

const dumbObjA = {
  type: EnumType.A
}

const dumbObjB = {
  type: 'B',
}

export function print() {
	console.log('hello')
}
javascript 复制代码
// main.js
import { print } from './module-a'

print()

表面上看 dumbObjAdumbObjB 都没有被引用,我们会期望 Rollup 打包之后的代码里 dumbObjAdumbObjB 都会被移除掉,但实际结果却是只有 dumbObjB 被清理掉,而 dumbObjA 以及 EnumType 都被保留了,这有些出乎意料:

javascript 复制代码
// Rollup 打包后的代码 index-XXX.js
var EnumType = /* @__PURE__ */ ((EnumType2) => {
  EnumType2["A"] = "A";
  return EnumType2;
})(EnumType || {});
({
  type: EnumType.A
});
function print() {
  console.log("hello");
}
print();

这正是上文所提及的:

引用的对象可能存在 side effect

由于 dumbObjA 的创建访问了 EnumType.A,Rollup 认为 EnumType.A 可能存在 side effect,因为 EnumType 是通过一个 IIFE(立即执行的函数表达式)创建的, EnumType.A 的访问可能触发 getter 从而执行一些 side effect 函数,为了保守起见,Rollup 保留的 dumbObjA 的代码。

作为开发者我们当然可以清楚地分辨出 EnumType.A 并没有 getter,可是 Rollup 并不如人聪明它无法分辨,因而为了保守起见它只能够保留这些可能有 side effect 的代码。

如果我们对 module-b.js 做一些修改,Rollup 才能够正确分辨出 EnumType 是没有 getter 的,可以放心清除掉它。

javascript 复制代码
// module-b.js
var EnumType = {
	A: 'A'
}

export {
	EnumType
}
javascript 复制代码
// Rollup 打包后的代码 index-XXX.js
function print() {
  console.log("hello");
}
print();

Why create EnumType using IIFE?

或许你会感到奇怪,如果一开始就不使用 IIFE 来创建 EnumType 的话,问题就不会存在。确实如此,但如果你使用的是 TS,而且不巧你用上了 TS 的 enum 关键字的话,那么最终 EnumType 就无可避免地被编译为 IIFE。至于为什么 TS 的 enum 为什么要编译成 IIFE,原因之一是 enum 支持声明合并的特性,这里也有些相关的讨论

typescript 复制代码
// module-b.ts
enum EnumType {
  A = 'A'
}

// 编译为 module-b.js
var EnumType
(function (EnumType) {
	EnumType['A'] = 'A';
})(EnumType)

export {
	EnumType
}

总结

长话短说,如果你是 TS 的开发者,而且你需要 TreeShaking 能够工作的话,考虑放弃 TS 的 enum 的使用。

References

相关推荐
excel14 小时前
nuxt 3 升级 nuxt 4 报错之 hasInjectionContext
前端
小磊哥er14 小时前
深入解构Claude Code - 第 7 篇 · 连接外面的世界
javascript·ai编程
何以解忧,唯有..14 小时前
LangChain 工具调用(Tool Calling)实战指南
java·前端·langchain
小磊哥er14 小时前
深入解构Claude Code - 第 6 篇 · 终端里的图形界面
javascript·ai编程
海兰14 小时前
【应用】基于 Next.js 16 + Python mplfinance的金融K线图与技术指标可视化平台(一)
javascript·python·金融
软件聚导航15 小时前
「聚小软 AI 助手」技术升级:从数据库检索到 RAG 知识库问答
前端·数据库·mysql·微信小程序·小程序·ai编程·rag
恋猫de小郭16 小时前
看懂大模型架构术语,帮助你理解目前常见的大模型开源架构
前端·人工智能·ai编程
yma1616 小时前
通过提示词实现GitHub Pages 和 Nginx 双部署竟然这么简单?
前端
可乐鸡翅yeah_16 小时前
hls.js 内存泄漏实战排查,直播 M3U8 长时间播放异常定位方案
开发语言·javascript·python·django·ecmascript·m3u8·m3u8在线
前端 贾公子16 小时前
第09章:上下文与记忆 (2)
java·服务器·前端