内容简介:Angular产品构建性能测试
翻译:Vincent
译者注:作者是一位从事于JavaScript开发的工程师,本文描述了作者立足于编译和绑定这两个因素,尝试不同方案对应用程序性能的影响。以下为译文。
在本文中,将讨论Angular应用程序的一些替代方案。将介绍几种不同的方法,并重点讲述它们的优点和缺点。
希望你能认真读完下面的内容,但是如果你觉得文章很长,不想细读,下面就展示了相关结果。
下面的表格显示了所有测试方案的结果:
在研究静态assets的时候,Angular应用程序中最重要的影响因素就是编译和绑定。
编译
Angular会将特定语法通过编译转换为浏览器可以理解的纯JavaScript。它可以将模板语法(如ngFor,ngIf等)转换为纯JavaScript。
编译有两种不同的形式:动态编译和静态编译。它们的区别不在于编译了什么,而在于什么时候开始编译的。
动态编译是在应用程序被下载以后,在浏览器端进行编译的,这不仅意味着在应用程序被渲染以前需要做大量额外的工作,还意味着在应用程序运行时需要装载另外一个编译器。
静态编译是在构建时进行编译的,所以解决了上述这些问题。此外,另一个好处就是在Angular运行时我们再也不需要包含其它的编译器了,正因为如此,bundle的大小也就明显减小了。
站在生产应用的角度来说,动态编译可能不是一个好的选择,但是由于它为我们提供了一个低端基准线可以进行比较,因此我还是会举一些相关例子。
我们使用的demo是一个中等大小的应用,是由我的一些Angular例子组成的。
Angular的性能在移动设备上是有问题的,所以我将在Chrome中使用扼制“Good 3G”来模拟一个性能差的设备,从而进行演示。
为了简单起见,所有报告的加载时间都来自Chrome浏览器的网络标签的“Finish”值。
由于我已经部署了所有示例的版本,所以可以随意尝试不同的指标。
动态编译
由于低端基准线的原因,我已经将应用程序部署成了一个标准的动态编译的项目。
应用部署到了 这里 。
正如你看见的那样,在应用被全部渲染之前,会有一个很明显的加载过程。如果你打开了浏览器的网络标签页,你就会知道原因了。
该应用程序需要发出163个请求,大约需要6秒才能完全加载。 相对于标准而言,这太慢了。
绑定的动态编译
就像我上面提到的那样,动态编译存在的一个问题就是仅仅为了加载应用它就需要发出163个请求。
在动态编译的构建期间,添加绑定应该就可以解决这个问题。
这次的样例位于 这里 。
我使用了SystemJS-Builder来进行绑定。SystemJS-Builder是SystemJS的一种工具,但是它是一款独立的工具,完全脱离SystemJS模块加载器。
你可以看见,效果明显提升了。动态编译再也不会发出163个请求了,而且还很明显地缩小了负载(260kb)。尽管如此,总的加载时间还是超过了2秒。
尽管前进了一些,但是渲染时的停顿感还是很明显。
静态编译
动态编译的问题已经解决的差不多了,但是性能问题还是比较棘手。
幸运的是我们并没有因此就放弃了。我们还可以通过选择静态编译来改善性能,对JavaScript进行进一步的优化应该也可以。
接下来,我们将研究更切合实际的生产方式,看看它们是如何提高性能的。
绑定
静态编译在性能方面有很多优点,但是关于绑定还有很多需要认真考虑的事。
Rollup
在动态编译的构建过程中,我尝试绑定了CommonJS模块。虽然它很灵活,但事实证明它并不适合用来绑定。相反我们应该使用ES2105模块。
ES2015模块更适合于一种名为“Tree shaking”的技术。
Tree shaking是在应用程序代码中遍历导入和导出语句的过程。它是Tree shaking的一种很好的实现,所以它成为了很多人的选择。
关于Rollup版本的应用程序可以点击 这里 。
正如你所看到的那样,Rollup这个版本明显比动态编译构建的版本更快。加载时间减少到大概只有1.3秒,大小只有147kb,比之前大概减少了43%,原因就是由于去掉了编译器和使用了Tree shaking。
Webpack
接下来我们将重复实验使用Webpack作为绑定。Webpack是另一种比较受欢迎的框架,但它的方法与Rollup是有着天壤的区别。
与Rollup相比,Webpack总是会产生一些较大的绑定。这是由Webpack会把每一个包含的模块整合成一个模块系统的方式决定的,这就导致了额外的函数包装器的绑定需要更多的开销。
Webpack不支持Tree shaking,这也就意味着没有任何机会去使用Tree shake了。Webpack与Tree shaking会有些令人混淆的内容,但是 这篇文章 会帮你理清楚的。
Webpack的相关程序在 这里 。
你可以看见,使用Webpack的性能跟Rollup差不多,但是体积明显增大了(151k ~增长了2.7%)。
这4K的区别不值得引起我们的注意,但是由于额外的开销,Webpack总是会更大一点。
同样不值得注意的是Angular中的NgModule架构阻碍了Tree shaking。这可能是我们在Rollup和Webpack之间看不到更大差异的原因之一。
如果你的代码从同一个文件导出多个类,您还会看到Webpack大小的增加。这是Webpack最糟糕的用例,因为它不能摆脱未使用的导出类。
Closure 编译器
目前为止所有的结果看上去都挺好的,但是可改善的空间还是很大的。
Webpack和Rollup都是传统的绑定。除了Tree shaking(Rollup)和minfication之外,它们提供的代码优化很少。
为了更好的优化,我们可以添加Closure编译器。
与它们相比,Closure编译器的主要区别在于它将对应用程序进行更深入的分析。 它可以通过功能内联,函数扁平化和删除部分代码等方法从而进行压缩。 这比minifcation更有效,从Closure编译的过程中就可以体验到。
这次的样例位于 这里 。
Closure仅仅只有97.4kb,比Webpack小了35%,太不可思议了!总的加载时间也就大概1秒钟。
Closure编译器的结果真是吓到宝宝了,但是这种优化是要付出一定代价的。编译器对您的代码做了几个假设。 除非您确保您的代码与Closure兼容,否则您的应用程序可能会中断。
Angular和其自定义的Typescript编译器使得编译变得容易一些了。 通过Angular的Typescript编译器,确保某些约定使得代码与Closure更加兼容。
Webpack
目前我们只讨论了单一捆绑应用。随着应用程序的发展,将整个应用程序作为单个JavaScript软件包进行服务可能行不通了。
Webpack跟其它的相比,提供了更多的灵活性。Webpack可以将应用程序分解成多个文件。这在您使用路由器的情况下就会更加突出了的,因为您可以在每个路由上创建一个包。这就给了你真正意义上的懒加载。
这就是Webpack提供的灵活性比其他选择都要多的地方。Webpack支持将应用程序分割成多个文件。当你在使用路由器的情况下为每个路由上创建绑定,就能看到它的优点了。这就给了你真正意义上的懒加载。
在最后一个例子中,我将演示应用程序转换为每个路由的绑定。
应用部署到了 这里 。
您可以看出,当您浏览应用程序时,单独的软件包将按需加载。除了路由特定的捆绑包,还有一个“共享”捆绑包。 共享捆绑是101kb。 默认路由将一个3.1kb的捆绑包添加到初始支付负载。
由于初始负载的总数只有104kb,所以我们非常接近于Closure编译器示例的结果。加载时间也很相似,只是一秒多一点。
这里的关键是将负载分散到多个请求中,而不是单个母版负载。所有bundle的总和都超过了单个的大小,这并不重要。通过按需加载小的和快速的,应用程序将是非常快的。
我想提一下,理论上也可以使用Closure编译器,但是在编写的时候设置它并不简单。一旦我们将延迟加载添加到一个Closure 构建中,我们可以期望得到更好的结果。
本文所有的例子都可以在 这里 找到。
以上就是本文的全部内容,希望本文的内容对大家的学习或者工作能带来一定的帮助,也希望大家多多支持 码农网
猜你喜欢:- 在 Android Studio 里使用构建分析器提升构建性能
- webpack构建和性能优化探索
- 构建高性能ASP.NET站点(上)
- 《高性能linux服务器构建实战》
- 构建高性能ASP.NET站点(下)
- 构建高性能ASP.NET站点(中)
本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们。
Algorithms + Data Structures = Programs
Niklaus Wirth / Prentice Hall / 1975-11-11 / GBP 84.95
It might seem completely dated with all its examples written in the now outmoded Pascal programming language (well, unless you are one of those Delphi zealot trying to resist to the Java/.NET dominanc......一起来看看 《Algorithms + Data Structures = Programs》 这本书的介绍吧!