什么是Tree-Sharking?
直接帖上一篇百度外卖的文章:Tree-Shaking性能优化实践 - 原理篇
如果不想看长篇文章,我简单讲一下:简单说tree-sharking就是在前端在打包时,去掉不需要的代码。
Tree-shaking 较早由 Rich_Harris 的 rollup 实现,后来,webpack2 也增加了tree-shaking 的功能。其实在更早,google closure compiler 也做过类似的事情。
Tree-shaking的本质是消除无用的js代码。无用代码消除在广泛存在于传统的编程语言编译器中,编译器可以判断出某些代码根本不影响输出,然后消除这些代码,这个称之为DCE(dead code elimination)。Tree-shaking 是 DCE 的一种新的实现,Javascript同传统的编程语言不同的是,javascript绝大多数情况需要通过网络进行加载,然后执行,加载的文件大小越小,整体执行时间更短,所以去除无用代码以减少文件体积,对javascript来说更有意义。
Tree-shaking 和传统的 DCE的方法又不太一样,传统的DCE 消灭不可能执行的代码,而Tree-shaking 更关注宇消除没有用到的代码。
Dead Code 一般具有以下几个特征
•代码不会被执行,不可到达
•代码执行的结果不会被用到
•代码只会影响死变量(只写不读)
传统编译型的语言中,都是由编译器将Dead Code从AST(抽象语法树)中删除,那javascript中是由著名的代码压缩优化工具uglify完成删除DCE。
ES6 module 特点:
- 只能作为模块顶层的语句出现
- import 的模块名只能是字符串常量
- import binding 是 immutable的
ES6模块依赖关系是确定的,和运行时的状态无关,可以进行可靠的静态分析,这就是tree-shaking的基础。
Webpack 4.0 以前,Tree Shaking 对于那些无副作用的模块不会生效。Webpack 4.0 开始,Tree Shaking 对于那些无副作用的模块也会生效了。
消除实验
面向过程编程函数和面向对象编程是javascript最常用的编程模式和代码组织方式,从这两个方面来实验:
- 函数消除实验
- 类消除实验
函数消除实验中,rollup和webpack都通过,符合预期
类消除实验中,rollup,webpack全军覆没,都没有达到预期
在函数和类消除试验中,google的closure compiler, tree-shaking的结果完美!
Google定义一整套注解规范Annotating JavaScript for the Closure Compiler,想更多了解的,可以去看下官网。侵入式这个就让人很不爽,Google Closure Compiler是java写的,和我们基于node的各种构建库不可能兼容(不过目前好像已经有nodejs版 Closure Compiler),Closure Compiler使用起来也比较麻烦,所以虽然效果很赞,但比较难以应用到项目中,迁移成本较大。
下面摘取了rollup核心贡献者的的一些回答:
- rollup只处理函数和顶层的import/export变量,不能把没用到的类的方法消除掉
- javascript动态语言的特性使得静态分析比较困难
关于副作用的讨论,有兴趣的同学可以看看这个:Tree shaking class methods · Issue #349 · rollup/rollupgithub.com
函数的副作用相对较少,顶层函数相对来说更容易分析,加上babel默认都是"use strict"严格模式,减少顶层函数的动态访问的方式,也更容易分析。
副作用是什么?
一个函数会、或者可能会对函数外部变量产生影响的行为。
举个栗子: