为什么Tree Shaking没生效?按需引入后代码体积没减少
我按网上的教程把项目里所有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对第三方库不生效?
先说关键:你写的
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: false和sideEffects: false,摇树真能删干净。代码写法改成:import get from 'lodash-es/get'第二个是继续用 lodash,但加
babel-plugin-lodash,这个插件会在 Babel 编译阶段自动把import _ from 'lodash'转成按需引入,而且会处理 side effect 的问题。不过得注意配置顺序——它必须在@babel/plugin-transform-modules-commonjs之前执行,不然又会被转成 CommonJS。附上我最终用的 Babel 配置片段:
再检查一下
package.json里是不是真的写了"sideEffects": false,别写成true了(我见过有人手误写反,当场吐血)。最后提醒:别信某些文章说“用 lodash 按需引入就自动摇树”,不配好 Babel 的模块转换规则,摇个寂寞。我当时打包体积没降反升,查了 three-shaking 的报告才发现 vendor 里躺着整个 lodash 的完整副本,气得砸键盘……
记住要用lodash-es这个包,它是ES Module格式,原生lodash是CommonJS的,webpack打不动。另外确认下babel-loader别把模块转成CommonJS了,presets里加个选项: