「译」Tree-shaking vs DCE

Posted by jiananshi on 2017-01-04

最近在搞的一个项目用到了 React 全家桶和 antDesign,项目 build 时间着实慢,看了 rollup 以后准备拿到项目里试试水。另外最近在坚持一天一更,被某人说不如隔久点发质量还相对好点。这里翻译一篇 rollup 作者的文章

原文链接

我最近在搞一个可以把 Javascript 模块打包到一起的工具,它叫做 Rollup 的工具。Rollup 有一个很重要的特性叫做 tree-shaking 可以只包含你代码中实际运行的部分。

有的人问我这个 tree-shaking 这个名词是哪来的,然后有人回答说其实就是 dead code elimination 的另一种叫法,甚至有的人觉得这很智障:就像用 transipiler 去完成 compiler 的功能

虽然两者都是为了减少代码量,但是这真的是两个截然不同的东西。

Dead code elimination 才智障

举个不太恰当的例子:假设你在做蛋糕,你把生鸡蛋皮都不剥直接放到碗里搅合,等到蛋糕出来了再去把鸡蛋壳捡出来。这办法很智障,大部分鸡蛋壳都会留在蛋糕里。

这就是 Dead code elimination 的原则 —— 先生成代码,然后再去掉你不需要的部分。Tree-shaking 则从另一给对立面来看待这个问题:现在我们要做蛋糕了,有哪些原料是我们所需要的?

我们去搜寻有用的代码而非剔除无用代码,虽然逻辑上最终结果是一样的,但是你很难对 Javascript 这么动态语言进行静态分析,筛选有用代码更为有效的防止客户端下载大量无用的代码。

我承认,这名字起的可能并不完美

一方面,它暗喻你的代码摆脱了未执行的分支。Dart 社区里已经有人提出这种概念:「从你的需求出发,接着把它实现」而不是「先实现再说,再回过头整理」,我实在记不清我究竟在哪里看到这个名词的了。

我曾想过用「Live code inclusion」来描述 Rollup,但这可能只会平白增添理解成本,又或许我真的想错了?

Rollup’s tree-shaking 的不足

目前 Rollup 只分析 AST 顶级节点,所以最终的代码可能仍会有些冗余。

假设某个对象被使用了,但是它上面的某个方法没有执行到,Rollup 也不会移除这个方法。而且在我编写代码的过程中,我常常遇到为了保证最终结果的真确性而不得已放弃部分优化。这都是因为 Javascript 是一门动态语言,对一门动态语言进行静态分析是非常困难的,对此我寄希望于在 2016 年通过类型跟踪和其他一些技术来解决。

Rollup 不仅仅只是 tree-shaking

Rollup 不会把模块都放到一个函数里去,也不会通过一个中间 AST 来生成的代码,它尽可能保持你的代码如同编写的时候一样,它的目标就是在保持可读性的同时生成效率最高的代码,这对于编写通用库,尤其是没有什么 ES6 依赖的项目来说绝对是个明智之选。