Tree Shaking 不会单独删除类属性,而是根据类是否被实际使用(如实例化、调用静态方法)决定整类保留与否;需显式导出并调用类、声明副作用文件、避免默认导出对象包装、对 HTML/运行时调用做 window 挂载与入口保活。

Tree Shaking 本身不直接“误删类属性”,真正被保留或剔除的,是类的导出、实例方法的调用路径、以及构造函数或静态成员是否产生可识别的副作用。类属性(如 class A { x = 1; y = () => {} })属于类定义的一部分,其是否保留,取决于整个类是否被引用,而非属性本身独立存在。
要防止类中本该生效的属性(尤其是带初始化逻辑、副作用或被 HTML/运行时动态访问的属性)被误删,关键不是“保护属性”,而是让打包工具明确:这个类或它的某些成员必须保留。
确保类及其关键属性被正确识别为“已使用”
- 类若只被
import但未new或未访问任何成员,它可能被整块摇掉(尤其当无导出、无副作用时)。 - 类字段(field)如
x = 1、y = () => {}是初始化表达式,在构造函数中执行;如果类没被实例化,这些代码根本不会运行——Tree Shaking 不会“单独保留某个字段”,只会保留整个类定义或整个模块。
✅ 正确做法:
- 显式导出类,并在其他地方有实际调用(如
new MyClass()或MyClass.staticMethod()) - 避免仅导入却不使用:
import { MyClass } from './a.js';但没后续调用 → 整个类可能被剔除
⚠️ 常见陷阱:
- 使用
export default class {}→ 没问题,但若入口文件没引用它,就会被删 - 类中字段初始化含副作用(如
api.init()、document.body.append(...)),但未声明sideEffects→ 整个模块可能被误删
对含副作用的类初始化逻辑做显式声明
如果类的字段初始化或构造函数执行了全局操作(比如注册事件、注入样式、修改 DOM),这属于副作用,必须让打包工具知道:
- 在
package.json中将该文件列入sideEffects数组"sideEffects": ["src/components/ThemeSwitcher.js"]
- 路径需相对于
package.json,且匹配实际参与构建的文件(通常是源码路径,Webpack/Vite 会自动映射)
例如:
// src/components/ThemeSwitcher.js
export default class ThemeSwitcher {
// 构造时自动切换主题,有副作用
constructor() {
document.documentElement.setAttribute('data-theme', 'dark');
}
}
→ 必须出现在 sideEffects 列表中,否则即使被 import,也可能因“无导出使用”被删。
避免把类包装进默认导出对象里
错误写法(阻断 Tree Shaking 精准性):
// utils.js
export default {
Button: class {},
Input: class {}
};
→ 即使只用 utils.Button,整个对象(含未用的 Input)都会保留。
✅ 推荐写法:
// utils.js
export class Button { /* ... */ }
export class Input { /* ... */ }
调用端:
import { Button } from './utils.js';
→ Input 可被安全摇掉。
HTML 或运行时动态调用时的保活策略
如果类实例由 HTML 中 onclick="new MyWidget()" 创建,ESM 默认不会识别这种引用:
必须显式
export类(哪怕没被 JS 导入)-
同时挂载到
window(双重保障):export class MyWidget { /* ... */ } window.MyWidget = MyWidget; Rollup/Vite 中启用
preserveEntrySignatures: 'exports-only',防止入口模块因无 JS 引用而被丢弃Webpack 中可为该文件配置
parser.sideEffects: true
不复杂但容易忽略。
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南











