简介
tree shaking
是一个术语,通常用于描述移除 JavaScript
上下文中的未引用代码(dead-code
)。它依赖于 ES2015
模块语法的 静态结构 特性,例如 import
和 export
。
这个术语和概念实际上是由 ES2015
模块打包工具 rollup 普及起来的,它是DCE(dead code elimination)
的一个实现,通过tree shaking
的分析,可以使你代码里没有使用的代码全部删除。然而它又区别于普通的DCE
,它是去找真正使用的代码,而不是从已有代码里删除代码。
作用
删除无用代码,缩小文件体积,提升加载速度。
原理
tree shaking
依赖的是ES6 module
。ES6 module
是编译时加载,编译时就可以确定模块的依赖关系,可以安全地做静态分析。并且具有以下特性:
- 只能作为模块顶层的语句出现
- import 的模块名只能是字符串常量
- import binding 是不变的
这就是 tree shaking
实现的基础。先通过import
的引入,静态分析依赖,然后删除无用代码。
示例
mode: "production"
模式下,默认打开tree shaking
我们先看个简单的例子,纯函数:
.
├── dist
│ └── main.js
├── package-lock.json
├── package.json
├── src
│ ├── index.js
│ └── util.js
└── webpack.config.js
复制代码
// util.js
export function a() {
console.log('this is a')
}
export function b() {
console.log('this is b')
}
复制代码
// index.js
import { a } from './util'
复制代码
在util.js
定义了两个方法,但是index.js
只引入了a
,并且没有调用,我们的预想是这些都不应该打包进去。
a
方法
// index.js
import { a } from './util'
a()
复制代码
可以看到a
已经被打包进去了,b
因为没有被引入调用,所以消除了。 这么看是不是很easy~
我们再测试一下类的消除
// person.js
class Person {
constructor() {}
sayName() {
console.log('I am daly')
}
sayAge() {
console.log('I am 18')
}
}
export default Person
复制代码
// index.js
import Person from './person'
复制代码
同样是没被打包进来的,完美~
我们调用一个方法试试
// index.js
import Person from './person'
let somebody = new Person()
somebody.sayName()
复制代码
咦~我只调用了sayName
,但是打包结果可以看出,这个类都被打包了。从这我们可以看出,类是不能分割打包的,即使方法之间没有依赖。
到这都只是简单的例子,看起来使用很简单,不需要我们思考,但是呢,去看官方文档,会发现提到了一个 side effect
副作用的概念。
"side effect(副作用)"
的定义是,在导入时会执行特殊行为的代码,而不是仅仅暴露一个export
或多个export
。举例说明,例如polyfill
,它影响全局作用域,并且通常不提供export
。
我来回读了几遍,都觉得它的意思是,有副作用的代码一定要记得标注在sideEffects
里,因为所在模块只引入没调用的话,生产模式下会被删掉的,这样特殊行为代码就执行不到了。但是,我发现最新的版本并非这样~~~
我们先理解一下,什么是有副作用的代码
// person.js
class Person {
constructor() { }
sayName() {
console.log('I am daly')
}
sayAge() {
console.log('I am 18')
}
}
Array.prototype.testSomething = function () {
console.log('this is side effect')
}
window.test = 'test'
export default Person
复制代码
除了export
的两个方法外,我还给Array
对象新增了一个方法,在window
上挂了一个全局变量。 当引入person
文件的时候,即使没有调用方法,这些方法也会执行的。这就可以称之为副作用。
// index.js
import Person from './person'
复制代码
那我们分析下上面的例子,person.js
文件被引入了,但是没有调用,按照之前的逻辑,是不是应该整个文件都不打包了?这样是不是就执行不到我们写的额外的函数了?
直接打包看一下~
exprot
的特殊行为代码段会被打包,但是没被调用的方法,一个都没打包进去! 我测试过在
package.json
里加上
sideEffects
参数,打包结果完全和上图一样。
我们再引入一个css文件试试
// index.css
* {
font-size: 24px;
}
复制代码
// index.js
import './index.css'
复制代码
无需把这个文件加入sideEffects
,也会被打包进去。
就是说tree shaking
现在已经十分智能,可以区分是否为副作用代码,实现一个比较理想的优化打包。
但是毕竟是写在官网上面的文档,不知道是不是我个人理解有误,之后需要再多尝试一下。
链接文章
webpack学习之路(九)SplitChunksPlugin配置
webpack学习之路(六)hash/chunkHash/contentHash
webpack学习之路(五)loader初识及常用loader介绍
webpack学习之路(四)webpack-hot-middleware实现热更新
webpack学习之路(三)webpack-dev-middleware
webpack学习之路(二)webpack-dev-server实现热更新
I am moving forward.