tree-shaking
什么是 tree-shaking
通过工具“摇”js 文件,将其中用不到的代码“摇”掉,属于性能优化。在 webpack 中有一个入口文件,相当于一颗树的主干,入口文件有很多依赖模块,在实际开发中,虽然依赖了某个模块,但是只使用了其中的某些功能。通过 tree-shaking 将没有使用的模块摇掉,来删除无用代码
支持 tree-shaking 的有
- Rollup
- Webpack2 开始
- Closure compiler
tree-shaking 起初是 rollup 实现的,后来 webpack2 也增加了这个功能。其实更早,goole closure compiler 也做过类似的事情。
原理
tree-shaking 本质是消除无用的 js 代码,其实消除无用代码广泛存在于传统的编程语言编辑器中。编辑器可以判断出某些代码无用,然后消除这些代码,称为 DCE
tree-shaking 是 DCE 的一种新的实现,js 同传统的编程语言不同的是 js 大多数情况下需要通过网络进行下载然后执行。加载的文件越小,整体的执行时间更短。所以去除无用代码减少文件体积对 js 来说意义巨大
但是 tree-shaking 和传统的 DCE 的方法又不太一样,传统的 DCE 消灭不可能执行的代码,而 tree-shaking 关注的是消除没有用到的代码。
区别
- DCE
- 代码不会执行,不可到达
- 代码执行的结果不会被用到
- 代码只会影响死变量(只写不读)
例子:
let foo = () => {
let x = 1
if (false) {
console.log('never reached')
}
let a = 3
return a
}
let baz = () => {
var x = 1
console.log(x)
function unused() {
return 5
}
return x
let c = x + 3
return c
}
baz()
传统编译型语言,都是由编译器将 Dead Code 从 ast 中删除,那 js 中由谁做 DCE 呢?
其实是由代码压缩优化工具 uglify,uglify 完成了 js 的 DCE
- tree-shaking
这个关注的是无用模块的消除,消除引用了但是并没有使用的模块。为什么 tree-shaking 是最近几年流行起来?而前端模块化概念已经有很多年历史了,其中 tree-shaking 的消除原理是依赖于 ES6 的模块特性
esm 的特点:
- 只能作为模块顶层的语句出现
- import 的模块名只能是字符串常量
- Import binding 是 immutable 的
esm 的依赖关系是确定的,和运行时的状态无关,可以进行可靠的静态分析。这是 tree-shaking 基础
- 静态分析就是不执行代码,从字面量上对代码进行分析。es6 之前的模块化,比如可以动态 require 一个模块,只有执行后才知道引用的是什么模块,这个就不能通过静态分析去做优化
消除实验:
在函数消除实验中,rollup 和 webpack 都通过,把没有用到的函数消除了
在类消除实验中,rollup、webpack 全军覆没,都没有达到预期
- rollup 只处理函数和顶层的 impport/export 变量。不能把没有用到的类消除掉
- js 动态语言的特性使得静态分析比较困难
- side Effect(副作用)广泛存在
tree-shaking 对函数效果较好
- 这些工具的 tree-shaking 对于无用代码,无用模块的消除都是有限的,有条件的。