讲一讲Webpack tree-shaking的触发机制
一、tree-shaking是什么?
这个词应该大家都不陌生了,翻译过来就是“树摇”。

看一下官方的解释:
Tree shaking is a term commonly used in the JavaScript context for dead-code elimination. It relies on the static structure of ES2015 module syntax, i.e.
importandexport. The name and concept have been popularized by the ES2015 module bundler rollup.
简言之,tree-shaking有助于我们减少打包后产物的体积,去掉无用的代码。这里不多说概念,本文的重点还是在于文章标题,我们关注的是webpack的tree-shaking能力以及触发条件。
二、搭建Webpack环境
创建项目
mkdir tree-shaking-webpack && cd tree-shaking-webpack
使用pnpm初始化package.json
pnpm init
安装webpack和webpack-cli
pnpm add webapack webpack-cli -D
创建webpack.config.js配置文件
// webpack.config.js
const path = require("path");
module.exports = {
entry: "./src/main.js",
output: {
path: path.resolve(__dirname, "./dist"),
filename: "bundle.js",
},
};
修改package.json
{
"name": "tree-shaking-webpack",
"version": "1.0.0",
"description": "",
"main": "./src/main.js", // 修改入口
"scripts": {
"build": "webpack --config ./webpack.config.js" // 增加build命令
},
"keywords": [],
"author": "",
"license": "ISC",
"devDependencies": {
"webpack": "^5.89.0",
"webpack-cli": "^5.1.4"
}
}
创建入口文件
// src/main.js
console.log('hello wolrd')
执行build命令,可以看到已经打包成功了

但是控制台报了个警告,原因是没有配置mode,我们配置mode为开发模式development即可
// webpack.config.js
const path = require("path");
module.exports = {
mode: "development", //开发模式
entry: "./src/main.js",
output: {
path: path.resolve(__dirname, "./dist"),
filename: "bundle.js",
},
};
再次执行build,控制台不再报错

三、webpack的tree-shaking
1、通过ES6的import和export触发
根据前面tree-shaking介绍中提到,tree-shaking其实依赖了ES6中的import和export。
所以如果我们先做第一个尝试:commonJS是否支持tree-shaking
我们新增个compute.js文件
// src/compute.js
const add = (a, b) => {
return a + b;
};
const sub = (a, b) => {
return a - b;
};
module.exports = {
add,
sub,
};
main.js中引用compute.js,并且只使用add方法
const { add, sub } = require("./compute");
console.log(add(1, 2));
执行打包后,发现main.js中sub方法也被打打包了

我们换成ES6规范的导入和导出
// src/compute.js
export default {
add,
sub
}
// src/main.js
import { add, sub } from './compute.js'
执行打包,main.js中不再出现sub方法

所以得出结论,如果要触发tree-shaking,需要通过ES6的import和export触发。
2、通过解构的方法触发
在满足第一点的条件下,我们试想一下,如果我们是import整个对象,是否会触发tree-shaking呢?例如:
import compute from './compute'
console.log(compute.add(1,2))
因为这个案例的打包文件体积比较小,我们看不太出来,我们尝试安装一个lodash来试一下,需要注意的是,我们需要安装的是lodash的ESM版本,即lodash-es,否则官方默认的lodash是commonJS规范
pnpm add lodash-es
修改main.js的内容
// src/main.js
import { get } from "lodash-es";
console.log(get({ a: 1 }, "a"));
执行打包,并创建index.html文件引入打包后的bundle.js

打开index.html,查看bundle.js的体积大小为103kb

如果我们不使用解构的方式,直接引入整个lodash,然后打包
// src/main.js
import _ from "lodash-es";
console.log(_.get({ a: 1 }, "a"));
再次打包后,可以看到,bundle.js的体积大小已经来到了恐怖的1.6MB

经过tree-shaking之后,bundle.js只打包了lodash的get方法,其他无关的方法都被“摇走了”

所以得出结论,如果要触发tree-shaking,需要通过解构的方式触发。
3、开启production模式
细心的同学其实可以发现,上面第一点案例中,通过import的方式导入了compute.js文件,main.js代码中虽然没有了sub方法,但是compute.js依然会把sub方法打包

这是因为webpack对同一文件中的tree-shaking必须要开启production模式才能生效
// webpack.config.js
const path = require("path");
module.exports = {
mode: "production", //生产模式
entry: "./src/main.js",
output: {
path: path.resolve(__dirname, "./dist"),
filename: "bundle.js",
},
};
再次执行打包,发现sub方法已经不存在了,而且代码还进行了压缩

4、总结
通过上面的几个案例,webpack如果要实现tree-shaking必须满足以下几个条件:
- 通过
ES6的import和export,包括npm包也必须是ESM - 通过解构的方式引入
- 同一文件下,需要开启
production模式
转载自:https://juejin.cn/post/7298966922329554995