>

高效快速地加载

- 编辑:至尊游戏网站 -

高效快速地加载

高效快速地加载 AngularJS 视图

2016/06/29 · JavaScript · 1 评论 · AngularJS

本文作者: 伯乐在线 - ThoughtWorks 。未经作者许可,禁止转载!
欢迎加入伯乐在线 专栏作者。

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

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

单页Web应用(SinglePage)

AngularJS 视图一般原理

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

图片 1

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

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

图片 2

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

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

图片 3

顾名思义,只使用一个页面的Web应用程序.单页面应用是指用户通过浏览器加载独立的HTML页面,Ajax加载数据页面无刷新,实现操作各种操作。

AngularJS 对视图加载的优化

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

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

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

图片 4

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

模板(template)

对AngularJS templateCache的优化

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

图片 5

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

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

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

图片 6

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

在AngularJS中,一个模板就是一个HTML文件。但是HTML的内容扩展了,包含了很多帮助你映射model到view的内容。

浏览器缓存

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

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

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

图片 7

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

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

图片 8

»  HTML模板将会被浏览器解析到DOM中。

总结

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

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

图片 9

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

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

图片 10

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

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

1 赞 2 收藏 1 评论

»  DOM然后成为AngularJS编译器的输入。

关于作者:ThoughtWorks

图片 11

ThoughtWorks是一家全球IT咨询公司,追求卓越软件质量,致力于科技驱动商业变革。擅长构建定制化软件产品,帮助客户快速将概念转化为价值。同时为客户提供用户体验设计、技术战略咨询、组织转型等咨询服务。 个人主页 · 我的文章 · 84 ·   

图片 12

»  AngularJS将会遍历DOM模板来生成一些指导,即,directive(指令)。

»  所有的指令都负责针对view来设置数据绑定。

通过下面的DEMO我们一起了解一下Angularjs 内置模板引擎:

<!--模板(template):
这里的模板是指前端模板,在angularjs 之外已经有非常丰富的JavaScript模板引擎了,例如artTemplate,Mustache,doT.js等。
Angularjs 内置有自己的模板引擎。
通过下面的DEMO我们一起了解一下Angularjs 内置模板引擎:-->
<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml">

<head>
    <meta charset="UTF-8">
    <title>angularjs 模板解释</title>
    <script src="http://files.cnblogs.com/files/Arrays/angular.min.js"></script>
    <script type="text/javascript">
        //声明一个私有函数域
        (function () {
            var app = angular.module("MyModule", []);  //创建模块
            app.controller("tmplController", ["$scope", function ($scope) {
                //给变量val赋值
                $scope.val = "Hello Angularjs template";
                //给变量list赋值
                $scope.list = [
                    { title: "cnblogs", url: "http://www.cnblogs.com" },
                    { title: "codeproject", url: "http://www.codeproject.com/" },
                    { title: "google", url: "http://www.google.com/" }
                ]
                //给变量hasValue赋值
                $scope.hasValue = false;
            }]);
        })()
    </script>
</head>

<body ng-app="MyModule">
    <div ng-controller="tmplController">
        <!--普通输出-->
        <div>{{val}}</div>
        <!--循环-->
        <ul ng-repeat="item in list">
            <li><a href="{{item.url}}" rel=nofollow>{{item.title}}</a></li>
        </ul>
        <!--条件语句-->
        <div ng-if="!hasValue">
            Angularjs条件语句
        </div>
    </div>
</body>

</html>
<!--以上代码首先创建一个"MyModule"模块,然后在模块中添加了一个名词为"tmplController"的控制器,然后给scope添加三个属性“val”,“list”,“hasValue”,并赋值。
在模板页面设置angularjs作用域为"body"标签内,名词为"Bobby"-->
<!--例子中提供了3种输出方式(普通输出,循环,条件语句),当然angularjs不仅仅这几种方式
“ng-repeat”,“ng-if” 这都是通过指令实现。-->

 

Controller(控制器)

Controller应该纯粹地用来把service、依赖关系、以及其它对象串联到一起,然后通过scope把它们关联到view上。如果在你的 视图里面需要处理复杂的业务逻辑,那么把它们放到controller里面也是一个非常不错的选择。

如果我要add一本书籍应该怎么办呢?我应该在controller上面新增一个方法来处理这件事情吗? 不,原因在下面解释。因为它是DOM交互/操作的一部分。

Controller(控制器)-注意点

不要试图去复用Controller,一个控制器一般只负责一小块视图

不要在Controller中操作DOM,这不是控制器的职责

不要在Controller里面做数据格式化,ng有很好用的表单控件

不要在Controller里面做数据过滤操作,ng有$filter服务

一般来说,Controller是不会互相调用的,控制器之间的交互会通过事件进行

<!--控制器(controller)控制器可以理解为控制页面某个区块的方法。
其中有一个非常重要的对象 (scope是这个控制器与页面控制器区域沟通的桥梁。
angularjs最精华的部分是双向绑定,失去了双向绑定angularjs就失去了自己的灵魂。这也是和其他以DOM操作的框架比最大的区别。-->
<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml">

<head>
    <meta charset="UTF-8">
    <title>angularjs控制器介绍</title>
    <script src="http://files.cnblogs.com/files/Arrays/angular.min.js"></script>
    <script type="text/javascript">
        (function () {
            var app = angular.module("MyModule", []);
            app.controller("cntoController", ["$scope", function ($scope) {
                var defaults = "angularjs控制器介绍";
                //设置值
                $scope.val = defaults;
                $scope.click = function () {
                    //通过内置的绑定方法click 我们重置字符串
                    $scope.val = defaults;
                };
            }]);
        })()
    </script>
</head>

<body ng-app="MyModule">
    <div ng-controller="cntoController">
        <!--绑定值-->
        <div>
            <textarea ng-model="val"></textarea>
        </div>
        <!--输出值-->
        <div>{{val}}</div>
        <!--绑定方法-->
        <div>
            <button ng-click="click()">重置</button>
        </div>
    </div>
</body>

</html>

<!--我们首先创建了一个模块,然后在模块中添加一个控制器方法 "cntoController".
在控制器里我们给scope添加了一个属性"val" 和一个方法 "click"。
在页面中我们使用"ng-model"指令绑定控制器中设置的"val"-->

 

本文由门户名站发布,转载请注明来源:高效快速地加载