Tree Shaking 为什么没生效?打包体积还是很大
我用的是 Vite + React,明明只 import 了 lodash 的 debounce,但打包后整个 lodash 都被打进去了,体积一点没减小。是不是 Tree Shaking 没生效?
我试过把 sideEffects: false 加到 package.json,也确认了构建模式是 production,但还是不行。下面是我的代码:
import { debounce } from 'lodash';
function MyComponent() {
const handleResize = debounce(() => {
console.log('resized');
}, 300);
return <div onResize={handleResize}>Content</div>;
}
是不是因为 lodash 本身不支持 ES Module 导致的?那我该换用 lodash-es 吗?
先说原理。Tree Shaking 的核心依赖是 ES Module 的静态分析,打包工具在编译阶段就能确定你用了哪些导出,然后把没用到的代码删掉。但 CommonJS 是动态加载的,
require可以写各种骚操作,打包工具在编译时根本没法静态分析你到底用了啥,所以 Tree Shaking 对 CommonJS 基本无效。lodash 这个库,默认导出的就是 CommonJS 格式。你虽然写的是 ES Module 的 import 语法,但打包工具内部会把它转换成类似 require 的行为,整个 lodash 对象都被拉进来了。
解决方案有两个。
方案一,换成 lodash-es,这是 lodash 的 ES Module 版本:
这个方案最干净,打包工具能完美识别并只打包你用到的函数。
方案二,如果你不想换包,可以精确导入单个模块:
lodash 的每个函数都有独立的文件,比如
lodash/debounce、lodash/throttle,这样导入只会拉取对应的模块文件。我个人推荐方案一,代码写起来更顺手,而且 lodash-es 的 Tree Shaking 效果非常稳定。记得装完 lodash-es 后把 package.json 里的 lodash 依赖删掉,别两个都留着,容易混淆。
另外顺便说一句,你代码里那个
onResize事件在 div 上是不会触发的,这个事件只有 window 对象支持。如果你是想监听元素尺寸变化,得用 ResizeObserver。不过这跟 Tree Shaking 没关系,我就不多展开了。