为什么Tree Shaking没生效?按需引入后代码体积没减少

司马朱莉 阅读 43

我按网上的教程把项目里所有lodash的全局引入都改成按需导入了,但打包后发现整体体积反而比之前更大?比如这样写:


import _get from 'lodash/get';
import _camelCase from 'lodash/camelCase';

function process(data) {
  const value = _get(data, 'user.name', 'default');
  return _camelCase(value);
}

我用webpack4配置了sideEffects: false,也尝试过用babel-plugin-lodash插件,但打包后的vendor.js里还是能看到整个lodash的代码块。难道按需引入需要额外配置?或者Tree Shaking对第三方库不生效?

我来解答 赞 5 收藏
二维码
手机扫码查看
2 条解答
欧阳利利
血泪教训啊兄弟,你这问题我当年踩得头皮发麻,不是 Tree Shaking 不生效,是 lodash 按需引入的写法压根没触发摇树条件,或者被 Babel 给吃掉了。

先说关键:你写的 import _get from 'lodash/get' 这种写法本身是对的,但问题出在 Babel 的处理上。Babel 默认会把 ES Module 的 import 转成 CommonJS 的 require,而 CommonJS 是静态分析不了的,Webpack 就根本不知道你到底用了哪些模块,Tree Shaking 就凉了。

你得确认两件事:第一,Babel 要配置成保留 ES Module 输出(presets: [['@babel/preset-env', { modules: false }]]),否则所有 import 都变成 require,摇树直接失效;第二,lodash 本身虽然标了 sideEffects: false,但它的包里那些 lodash/get.js 文件其实也写了 side effect(比如模块顶层有赋值操作),Webpack 保守起见不敢删。

我后来试过一堆方案,最靠谱的其实是两个:

第一个是用 lodash-es 替代 lodash,它专门做了 ESM 版本,没有 side effect,配合 modules: falsesideEffects: false,摇树真能删干净。代码写法改成:

import get from 'lodash-es/get'

第二个是继续用 lodash,但加 babel-plugin-lodash,这个插件会在 Babel 编译阶段自动把 import _ from 'lodash' 转成按需引入,而且会处理 side effect 的问题。不过得注意配置顺序——它必须在 @babel/plugin-transform-modules-commonjs 之前执行,不然又会被转成 CommonJS。

附上我最终用的 Babel 配置片段:

{
"plugins": [
"lodash",
["@babel/plugin-transform-modules-commonjs", { "allowTopLevelThis": true }]
],
"presets": [
["@babel/preset-env", { "modules": false }]
]
}


再检查一下 package.json 里是不是真的写了 "sideEffects": false,别写成 true 了(我见过有人手误写反,当场吐血)。

最后提醒:别信某些文章说“用 lodash 按需引入就自动摇树”,不配好 Babel 的模块转换规则,摇个寂寞。我当时打包体积没降反升,查了 three-shaking 的报告才发现 vendor 里躺着整个 lodash 的完整副本,气得砸键盘……
点赞 2
2026-02-27 17:05
 ___思晨
问题出在lodash的引入方式上,改成这样就能正确Tree Shaking了:

import get from 'lodash-es/get';
import camelCase from 'lodash-es/camelCase';

function process(data) {
const value = get(data, 'user.name', 'default');
return camelCase(value);
}


记住要用lodash-es这个包,它是ES Module格式,原生lodash是CommonJS的,webpack打不动。另外确认下babel-loader别把模块转成CommonJS了,presets里加个选项:

{
test: /.js$/,
loader: 'babel-loader',
options: {
presets: [
['@babel/preset-env', { modules: false }]
]
}
}
点赞 5
2026-02-16 13:04