custom elements 不参与 tree shaking,因其无 export、非模块、注册为运行时副作用;需封装为 esm、延迟注册、声明 sideeffects 才可被摇除。

HTML 自定义元素(Custom Elements)本身不参与 Tree Shaking —— 它们不是模块,没有 export,也没有静态依赖图,打包工具根本不会、也不能对它们做摇树。
为什么 Custom Elements 不会被 Tree Shaking
Tree Shaking 是构建期的静态分析行为,只作用于 ES 模块(import/export)构成的依赖链。而自定义元素注册代码(如 customElements.define('my-button', MyButton))通常写在顶层脚本中,属于运行时执行逻辑:
-
customElements.define()是全局副作用调用,打包工具无法判定它是否“被需要”——哪怕组件没在 HTML 中出现,只要这行代码存在,就会被保留 - 即使你用
export class MyButton extends HTMLElement,但没被任何import引用,Rollup/Webpack 仍可能因无法确认注册时机或副作用影响而不敢剔除(尤其当类里有顶层console.log或localStorage访问时) - HTML 中的
<my-button></my-button>标签是纯字符串,构建工具不解析其语义,也不触发模块分析
怎样让自定义元素“可被摇掉”
关键不是“对 HTML 做 Tree Shaking”,而是把自定义元素变成真正参与模块图的 ES 模块,并切断不必要的副作用:
- 把每个自定义元素封装成独立的
.js文件,且只导出类(如export class MyButton extends HTMLElement),不立即调用customElements.define() - 在应用入口统一注册,且仅注册实际用到的元素:
import { MyButton } from './MyButton.js'; customElements.define('my-button', MyButton); - 确保第三方自定义元素库提供 ESM 版本(检查
package.json中是否有"module"字段),避免引入整包(比如import { LitElement } from 'lit'而非import lit from 'lit') - 在
package.json中声明"sideEffects": false(或数组列出有副作用的文件),否则 Webpack 可能为安全起见保留整个模块
常见误操作与后果
这些做法会让 Tree Shaking 失效,体积不降反升:
- 在自定义元素文件顶部直接写
customElements.define('x-foo', XFoo):强制执行,打包工具视为必留副作用 - HTML 中用
<script type="module" src="x-foo.js"></script>单独加载:脱离主模块图,Rollup/Webpack 不追踪该文件是否被引用 - 通过
data-element="x-foo"+ 运行时document.querySelectorAll('[data-element]')动态注册:构建期无法静态识别,所有元素类都会被打包进去 - 使用
export * from './x-foo.js'导出多个元素:即使只用了其中一个,也可能阻止 shaking(改用具名导出export { XFoo, XBar }并按需 import)
真正决定体积的是 JS 模块的导入方式和副作用控制,不是 HTML 标签写了几个。别在 <template></template> 或 innerHTML 字符串里找“摇树”机会——那是运行时的事,跟构建优化无关。
前端入门到VUE实战笔记:立即使用
在学习笔记中,你将探索 前端 的入门与实战技巧!











