Tree Shaking 仅适用于 ESM JavaScript 模块,HTML 无 import/export、无静态依赖关系,无法被摇树;所谓“HTML Tree Shaking”实为误解,常见混淆包括预加载、条件渲染、服务端模板或 HTML 压缩等。
HTML 本身不支持 Tree Shaking —— 它不是模块系统,也没有 import/export 语法,因此无法被摇树。所有声称“对 HTML 做 Tree Shaking”的说法,本质上都是误解或误用术语。真正能被 Tree Shaking 的,只有 JavaScript 模块(且必须是 ESM)。
Tree Shaking 依赖静态分析:打包工具(如 Webpack、Rollup、Vite)扫描 import 和 export 语句,构建依赖图,再标记未引用的导出项并剔除。而 HTML 是纯声明式文档:
<script src="..."></script> 是运行时加载,无法在构建期静态判定是否“被使用”<link rel="stylesheet">、<img> 等资源引用不参与 JS 依赖图分析用户常把以下几类操作误认为“HTML Tree Shaking”,实际原理完全不同:
rel="preload" / rel="preconnect"):只是告诉浏览器提前发起请求,不删代码v-if 或 {condition && <div>}):运行时逻辑,与构建期摇树无关html-minifier-terser 等工具,和 Tree Shaking 无任何关系虽然 HTML 不被摇,但它可能间接破坏 Tree Shaking,导致本该被剔除的 JS 代码被强制保留:
立即学习“前端免费学习笔记(深入)”;
<script type="module" src="utils.js"></script>:如果 utils.js 使用 ESM 但被直接 script 引入,且内部有副作用(如修改 window),Webpack 可能因无法确认安全性而不敢摇掉它<script src="bundle.js"></script> 引入的 bundle 若含 CommonJS 模块(如 require('lodash')),整个库都会进包,无法局部剔除data-* 属性或内联 onclick 动态调用函数(如 onclick="doSomething()"),会让打包工具误判为“被使用”,阻止摇树别盯着 HTML 看——直接查 JS 构建产物和配置:
import { debounce } from 'lodash-es',而不是 import _ from 'lodash'
package.json 中第三方库是否有 "sideEffects": false(如 lodash-es 有,lodash 没有)npx webpack-bundle-analyzer dist/stats.json,看未使用的函数是否真的没出现在最终 chunk-vendors.js 里webpack --mode=production,开发模式下 usedExports 默认不启用最易被忽略的一点:哪怕所有 JS 都写对了,只要 HTML 里有一处 eval()、Function('...') 或内联事件处理器调用了某个函数名,打包工具就可能放弃对该模块做 Tree Shaking —— 因为它无法静态保证该函数“真的没被用到”。