Tree Shaking 为啥没把没用的函数干掉?
我用 Webpack + ES Module 写了个工具库,明明只 import 了一个函数,打包后却把整个文件都打进去了,Tree Shaking 没生效?
我试过加 sideEffects: false,也确认用了 production 模式,但还是不行。是不是写法有问题?
// utils.js
export const add = (a, b) => a + b;
export const minus = (a, b) => a - b;
export const unusedFn = () => console.log('never used');
// main.js
import { add } from './utils.js';
console.log(add(1, 2));
问题出在 Webpack 默认对 ES Module 的 Tree Shaking 识别是基于副作用检测的,但你的代码里虽然没副作用,Webpack 可能没识别出来,或者你没让 Webpack 知道这个包是“纯的”。
先说结论:你用的写法本身没问题,但要让 Tree Shaking 生效,至少得满足以下条件:
1. 你的代码必须是 ES Module(你这个没问题,用的是
export和import)2. 必须用
production模式(你确认了,OK)3. 关键点来了:你的入口文件不能引入任何 side effect(副作用),比如
console.log(add(1,2))这个本身不是副作用,但如果你在utils.js里有顶层的执行代码,比如console.log('loaded'),那整个模块就会被标记为有副作用,Tree Shaking 就失效了——不过你这个文件里没有,所以不是这个问题。真正容易踩的坑是:Webpack 默认不会对 node_modules 里的包做 Tree Shaking,但你这是本地文件,所以也不是这个问题。
那问题在哪?—— 你可能没配置
optimization.usedExports为true,或者没用 Terser 做二次压缩。Webpack 的 Tree Shaking 分两层:
- 第一层是静态分析,靠
optimization.usedExports(默认在 production 模式下是true,但如果你手动改过配置,可能被关了)- 第二层是压缩阶段,Terser 会读取这个标记,把没用的 export 删掉
你如果没配 Terser,或者用了其他压缩插件(比如
esbuild),它可能不认 Webpack 的usedExports标记,导致没用的代码还在。你先确认下 Webpack 配置里有没有这句:
再确认下有没有配 TerserPlugin(或者类似压缩器):
不过你这个例子其实更简单——你直接打包完看
dist/bundle.js里是不是还有unusedFn?如果还在,大概率是你没用 Terser,或者用了其他不支持 ES Module dead code elimination 的压缩器(比如babel-minify旧版就不行)。还有一个容易被忽略的点:如果你用的是 Webpack 4,必须用
sideEffects: false放在package.json里,而不是webpack.config.js里。你提到了加了sideEffects: false,但得确认是不是加在package.json顶层:如果你是库,建议再加个
"module": "dist/index.js"指向 ES Module 入口,不然 Webpack 可能走main字段,而main指向的是 CommonJS,Tree Shaking 就完全失效了(CommonJS 不支持静态分析)。最后说个真实经历:我当年也以为加了
sideEffects: false就完事,结果打包出来还是全量引入,后来发现是打包入口用了import * as utils from './utils',这种写法 Webpack 会认为“你用了整个模块”,就不会 Tree Shaking 单个导出。你检查下
main.js是不是用了import * from或者解构里写了多个名字?你这个例子是没问题的,但这是高频坑。总结下:
- 用 ES Module 写法 ✅
- production 模式 ✅
-
sideEffects: false在package.json✅- Webpack 配置里
usedExports: true✅- 用 Terser 或兼容 ES Module 的压缩器 ✅
- 没用
import * from这类破坏 Tree Shaking 的写法 ✅满足这些,
unusedFn就会被干掉。如果还还在,直接看dist/bundle.js顶部有没有/* unused harmony export unusedFn */这类注释——有说明 Webpack 标记了没用,但压缩器没删,那是压缩器的问题;没有的话,说明 Webpack 没识别出来,那是 sideEffects 或入口问题。赶紧试试,有问题再贴配置我帮你瞅一眼。