Tree Shaking 为什么没生效?打包体积还是很大

FSD-瑞君 阅读 12

我用的是 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 吗?

我来解答 赞 2 收藏
二维码
手机扫码查看
1 条解答
艳青
艳青 Lv1
你猜对了,问题就出在 lodash 的模块规范上。

先说原理。Tree Shaking 的核心依赖是 ES Module 的静态分析,打包工具在编译阶段就能确定你用了哪些导出,然后把没用到的代码删掉。但 CommonJS 是动态加载的,require 可以写各种骚操作,打包工具在编译时根本没法静态分析你到底用了啥,所以 Tree Shaking 对 CommonJS 基本无效。

lodash 这个库,默认导出的就是 CommonJS 格式。你虽然写的是 ES Module 的 import 语法,但打包工具内部会把它转换成类似 require 的行为,整个 lodash 对象都被拉进来了。

解决方案有两个。

方案一,换成 lodash-es,这是 lodash 的 ES Module 版本:

import { debounce } from 'lodash-es';

function MyComponent() {
const handleResize = debounce(() => {
console.log('resized');
}, 300);

return
Content
;
}


这个方案最干净,打包工具能完美识别并只打包你用到的函数。

方案二,如果你不想换包,可以精确导入单个模块:

// 直接导入 debounce 这个单独的模块文件
import debounce from 'lodash/debounce';

// 用法和之前一样
function MyComponent() {
const handleResize = debounce(() => {
console.log('resized');
}, 300);

return
Content
;
}


lodash 的每个函数都有独立的文件,比如 lodash/debouncelodash/throttle,这样导入只会拉取对应的模块文件。

我个人推荐方案一,代码写起来更顺手,而且 lodash-es 的 Tree Shaking 效果非常稳定。记得装完 lodash-es 后把 package.json 里的 lodash 依赖删掉,别两个都留着,容易混淆。

另外顺便说一句,你代码里那个 onResize 事件在 div 上是不会触发的,这个事件只有 window 对象支持。如果你是想监听元素尺寸变化,得用 ResizeObserver。不过这跟 Tree Shaking 没关系,我就不多展开了。
点赞
2026-03-02 14:02