高效快速地加载 AngularJS 视图|TW洞见

今日洞见

文章作者、部分图片来自ThoughtWorks:陈计节。

本文所有内容,包括文字、图片和音视频资料,版权均属ThoughtWorks公司所有,任何媒体、网站或个人未经本网协议授权不得转载、链接、转贴或以其他方式复制发布/发表。已经本网协议授权的媒体、网站,在使用时必须注明"内容来源:ThoughtWorks洞见",并指定原文链接,违者本网将依法追究责任。

当AngularJS应用程序变大时,很多问题就开始显现出来了,比如多层级视图的加载问题。如果在子视图显示之前没有预加载,则可能在需要展示时发生视觉闪烁的情况。这种问题在网络缓慢,或者服务器使用较慢的https连接时更容易出现。

本文将讨论更高效加载AngularJS视图的系统方法。

AngularJS 视图一般原理

AngularJS视图也并不是什么特别神奇的技术,在其内部就是按普通的directive来处理的。也就是说,当一个位置需要显示view时,AngularJS会尝试使用某种方法获得其HTML模板文件的具体内容,包装成directive,执行directive的标准流程,最后添加到页面上。

回想一下,directive本身是不是正好也支持templateUrl属性?这就与view技术衔接上了。

这样说来,是不是视图模板也可以使用行内DOM甚至是字符串字面量值了呢?答案是肯定的!我们本来就可以使用一段行内DOM来作为view的模板。例如:

当然,作为一个大型的AngularJS应用程序,将所有view都放在字符串值里,或者行内DOM里是不太现实的,我们希望可以使用多个小的HTML文件来作为子模板。这样,虽然整个应用很大,但每个子模板的文件并不大,一般都是几KB的小文件,当用户点击到指定位置,需要时使用对应界面的模板时再去加载,也就显著提高了效率。

我们可以用下图来表示“行内DOM”与“多个子模板文件”的性能对比:

AngularJS 对视图加载的优化

上面提到了“多个子模板文件”的模板组织方式,这本是一件很平常、很自然的工作方式而已。也正是因此,才让人们感觉AngularJS工作方式与自己的期望的一致:因为在没有使用AngularJS之前,人们在开发一个 Web应用时,页面就是这样一个个组织的。

即使在以前,我们在提到性能的时候,自然会想到“缓存”。在以前,页面与页面之间的跳转使得每个页面都是相互独立的单位,因此页面内容的缓存只能有赖于浏览器了。而今,AngularJS让所有页面子模板都在“单页应用”中加载,于是,我们在这个单页面应用内便获得了缓存页面内容的机会。

AngularJS中内建了缓存机制templateCache:只要已经加载过某个页面子模板,就会在templateCahce中缓存起来,下次从服务器加载页面模板之前,先检查templateCache,如果已有缓存则不需要从服务器上加载,直接使用。

AngularJS中内建了templateCache 机制之后,加载视图的过程变得高效而轻松,Web应用本身,以及开发者都不需要关心这一过程。不过,即使有页面内的templateCache,页面模板在初次使用时还是需要从服务器加载,因此偶尔能见到一些视觉闪烁的情况,比如标签切换、页面跳转等。

对AngularJS templateCache的优化

作为一种优化手段,我们很自然能想到,既然页面模板文件加载过一次之后,再次加载时直接从 templateCache 读取就能提高性能。如果在应用启动之初templateCache中就有了所有页面的缓存,也就根本不需要服务器了,那么在页面需要显示时,也就基本不需要加载时间了。图可以变成这样:

要实现这一目标,只需要在发布应用之前,构建额外的templates.js文件,在其中将所有的页面模板读取出来并提前put到templateCache中,再将形成的templates.js嵌入到应用中,即可在Web应用启动时就拥有所有页面模板内容的缓存版本了。

不过,对于大型AngularJS Web应用来说,我们很快发现一个问题:这个templates.js文件本身的体积迅速大了起来,又会引发新的性能问题。

为此,我们可以使用另一个已有的经验:“异步加载”。有了异步加载的支持,在加载templates.js的请求还没有完成之前,可以“降级”使用AngularJS内建的机制,而一旦templates.js加载完成,就立即拥有了所有模板的缓存。

理想中,templateCache最好能达到最佳的性能表现,但实际应用中,如果不加优化,templates.js文件本身的体积会令这种优化打折扣,而加上异步加载 templates.js和降级到逐个加载单个htm模板文件之后,又有了一些改善。

浏览器缓存

现在再来讨论一下浏览器缓存,可以结合上一节的templates.js一起来讨论了。浏览器缓存是浏览器里内置的一种缓存功能,当服务器正确配置了对htm和js文件的缓存支持时,浏览器将按指示缓存这些文件。无论是一个个htm模板,还是templates.js,都可能被缓存。

也就是说,只要在服务器上正确配置,那么上一节所述的“异步templates.js”,以及“降级的多个htm模板文件”都可以被浏览器缓存。这样,我们将加载htm模板文件和templates.js的需求都减少到第一次使用应用之时。

但在服务器上配置缓存也需要谨慎,如果配置不当,就会出现当服务器上文件已经更新,但客户端浏览器仍在使用老的缓存版本的问题。由于AngularJS应用使用绑定表达式显示界面,因此如果程序已经更新,而视图还是老版本,那么绑定表达式很可能失效。这种情况下,轻则局部界面错乱,重则整个Web应用完全无法使用。

浏览器缓存原本是一个“杀手锏”,不管是只使用单个模板文件,还是使用templateCache,浏览器缓存都可以极大地改善其性能效果。但一旦缓存配置不当致使客户端浏览器里使用了错误的版本,就直接导致应用错误,更不谈性能表现了。

要处理缓存问题也有成熟的经验可供借鉴:也就是在文件名上使用版本号,每次需要更新文件内容时,同时更改版本号,那么整个文件名也就发生变化,也就不会发生缓存版本错误问题。结合上面的论述,我们在templates.js上添加上版本号,另一方面配置AngularJS,在加载单个htm模板文件时,也会在请求上附上版本号,即可解决这一问题。当然,我们希望在开发时,标记要使用的视图模板时,不需要指定这个需要经常变化的版本号,从而最大程度地保障开发体验,并将维护成本降到最低。

总结

上面讨论了AngularJS视图各种可能的方式,分别实施的方法,以及其性能表现差异。主要值得关注的是经优化的templateCache机制,以及结合浏览器缓存的templateCache方法。总结来说,可以形成这样一个更直观的图形:

经过一番努力,最终我们能够达到这样的结果:

  1. 在应用里添加仅在生产环境才生效的策略:支持在加载视图模板文件时在文件名中添加版本号(从页面中templates.js的文件路径中分析版本号);
  2. 开发时不需要经过改变;
  3. 发布时预读取所有模板的内容,并生成带版本号的templates.js,嵌入应用页面中;
  4. 在服务器上配置所有htm模板文件及templates.js的缓存策略为“允许缓存”;
  5. 用户首次使用应用时,集中所有网络带宽加载AngularJS基础脚;本,以及应用程序业务逻辑系统,令应用程序尽早能够使用;此时应用使用htm模板文件作为视图模板;
  6. 异步加载templates.js;加载完成之后应用开始使用页面内模板缓存;
  7. 用户再次使用应用时,从浏览器缓存中加载templates.js;
  8. 再次发布应用时,修改templates.js 文件名中的版本号,嵌入页面中。

所以,在首次用户使用应用时,其网络加载图形就像这样:

最先加载的是应用程序AngularJS框架本身,以及业务逻辑,这时候应用已经可用;此时再异步去加载templates.js文件。事实上,上面的图形即是我们实际项目中的状况,具体实现在这里就不贴了,也欢迎读者一起探讨更多的可能性。

从本文的讨论中不难看出,只要通过各种方法,好好管理浏览器的加载行为,形成一个系统方法,便能令视图加载的性能表现变得更好。

原文发布于微信公众号 - 思特沃克(ThoughtWorks)

原文发表时间:2016-06-28

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

发表于

我来说两句

0 条评论
登录 后参与评论

相关文章

来自专栏ytkah

织梦DEDECMS后台精简删除不需要的文件

如果是一开始就不想要的话,安装版plus目录下进行如下操作。 删除:guestbook文件夹【留言板,后面我们安装更合适的留言本插件】; 删除:task文件夹和...

5824
来自专栏smartguys

(四):C++分布式实时应用框架——状态中心模块

  版权声明:本文版权及所用技术归属smartguys团队所有,对于抄袭,非经同意转载等行为保留法律追究的权利!

1575
来自专栏云计算教程系列

Linux备份工具简介

备份涵盖的范围很广,我们可以备份出一个重要文件的副本,也可以备份出一个完整的磁盘的快照。许多桌面应用程序和操作系统会自动进行数据备份。相比之下,腾讯云是一个灵活...

2521
来自专栏Java后端生活

Linux(二)CentOS的安装

3155
来自专栏数据和云

10分钟搭建MySQL Binlog分析+可视化方案

日志服务最近在原有 30+ 种数据采集渠道 基础上,新增 MySQL Binlog、MySQL select 等数据库方案,仍然主打快捷、实时、稳定、所见即所得...

2443
来自专栏大魏分享(微信公众号:david-share)

VMware软件定义数据中心分析工具介绍----第三终结篇

在本文连载的第一篇中,我向大家介绍了vROps的基本架构、vROps的指标含义,以及如何自定义告警。第二篇中,介绍了如何自定义仪表盘。在本篇中,我将介绍如何自定...

3788
来自专栏idba

ZanDB 开发清单

一 简介 从今年3月份开始,我和另外一位小伙伴王航威一起开发一套 数据库管理平台-ZanDB ,该平台主要使用Django 作为web 框架,使用 一款go语...

933
来自专栏熊训德的专栏

Hbase Replicaition 在腾讯云中应用概述

Hbase 的 Repliation 是通过 Zookeeper 的协助,从 Master 集群异步往 Slave 集群写 WAL 实现的。可以实现典型的 Ma...

6965
来自专栏Spark学习技巧

调试flink源码

本文主要是讲讲flink的源码编译,案例运行,flink源码调试过程。调试flink的源码及案例,需要先clone工程,编一下源码,去掉规范检查,修改工程,最后...

3375
来自专栏月色的自留地

mac电脑进行可见光通信实验要点

1106

扫码关注云+社区