一、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.
import
andexport
. The name and concept have been popularized by the ES2015 module bundler rollup.
简言之,tree-shaking
有助于我们减少打包后产物的体积,去掉无用的代码。这里不多说概念,本文的重点还是在于文章标题,我们关注的是webpack
的tree-shaking
能力以及触发条件。
二、搭建Webpack环境
创建项目
cmd
mkdir tree-shaking-webpack && cd tree-shaking-webpack
使用pnpm
初始化package.json
cmd
pnpm init
安装webpack
和webpack-cli
cmd
pnpm add webapack webpack-cli -D
创建webpack.config.js
配置文件
javascript
// webpack.config.js
const path = require("path");
module.exports = {
entry: "./src/main.js",
output: {
path: path.resolve(__dirname, "./dist"),
filename: "bundle.js",
},
};
修改package.json
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"
}
}
创建入口文件
javascript
// src/main.js
console.log('hello wolrd')
执行build
命令,可以看到已经打包成功了
但是控制台报了个警告,原因是没有配置mode
,我们配置mode
为开发模式development
即可
javascript
// 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
文件
javascript
// 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
方法
javascript
const { add, sub } = require("./compute");
console.log(add(1, 2));
执行打包后,发现main.js
中sub
方法也被打打包了
我们换成ES6
规范的导入和导出
javascript
// 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
呢?例如:
javascript
import compute from './compute'
console.log(compute.add(1,2))
因为这个案例的打包文件体积比较小,我们看不太出来,我们尝试安装一个lodash
来试一下,需要注意的是,我们需要安装的是lodash
的ESM
版本,即lodash-es
,否则官方默认的lodash
是commonJS
规范
cmd
pnpm add lodash-es
修改main.js
的内容
javascript
// src/main.js
import { get } from "lodash-es";
console.log(get({ a: 1 }, "a"));
执行打包,并创建index.html
文件引入打包后的bundle.js
打开index.html
,查看bundle.js
的体积大小为103kb
如果我们不使用解构的方式,直接引入整个lodash
,然后打包
javascript
// 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
模式才能生效
javascript
// 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
模式