继上一篇的angular性能优化一文中说道:有些项目场景是只需要单次绑定,减少性能开销的。不久之前,AngularJS 1.3版本正式发布,其中添加了很多的性特性,同时也对之前的一些bug做了修复,最重要的是其中包含一些突破性的变化。其中最重要的一项就是添加了一次性数据绑定。
等等,一次性数据绑定?这听起来似乎和AngularJS一直宣扬的双向数据绑定思想完全背离了吗?当然,AngularJS的双向数据绑定一直是AngularJS最重要的特性之一。然而,为了实现双向数据绑定,AngularJS需要为每一个绑定的值都设置一个监听器(watcher)。当需要绑定的数据越来越多时,成堆的监听器会对应用的性能造成很坏的影响。在具体了解一次性数据绑定之前,先让我们来简单的回顾一下AngularJS中双向数据绑定究竟是怎么一回事。
为了实现双向数据绑定,AngularJS使用了$watch
API来观察期作用域中的模型变化。具体的作用域取决于你的代码如何编写。如果你没有创建一个自作用域,就是说没有使用ngController
指令在你的DOM和你的控制器代码之间创建一个关联,你可能处理的是跟作用域$rootScope
,这个作用域由ngApp
自动创建,并且是应用中所有作用域的父作用域,当然,如果你选择手动启动AngularJS,那情况就另当别论了。
每当你创建了一个数据绑定时,AngularJS就会自动创建一个监听器来监听这个数据的变化。比如说下面这个简单的例子:
<p>Hello {{name}}!</p>
在这个例子中,我们使用了插值指令,这个指令会注册一个监听器来监听相应作用域中属性name
的变化,并在该属性发生变化时将它实时反应到DOM中。
添加下面的代码,你的name
属性将会自动的被赋值为Pascal
:
angular.module('myApp', [])
.run(function ($rootScope) {
$rootScope.name = "Pascal";
}]);
通过上面的代码,我们就成功的使用了插值指令创建了在试图上创建了一个数据绑定。现在,如果name
属性发生变化,视图将会自动发生更新。比如说我们添加下面的代码,在按钮点击是修改name
的值:
<button ng-click="name = 'Christoph'">Click me!</button>
此时,点击按钮,我们可以将name
的值修改为Christoph
,同时我们会触发一个$digest
循环来更新DOM中相应的部分。在上面的例子中,你看的知识单向数据绑定。然而,你完全可以使用ngModel
指令来将视图中发生的变化实时的反应到模型中。
上面的双向绑定魔法的实现完全依赖于$digest
循环,当$digest
循环被触发时,AngularJS将会去处理遍历当前作用域和子作用域中的所有监听器,然后通过检查模型中发生的变化来更新DOM中的值,直到模型不再发生变化为止。一旦$digest
循环执行完成,浏览器会重新渲染DOM来反应模型数据变化。
现在,我们大概了解了AngularJS的数据绑定机制,你可能会问我们为何还需要一次性数据绑定。
由于AngularJS使用监听器来实现数据绑定。当监听器越来越多时,可能会出现一些性能上的问题。由于在注册监听器使,同时会注册一个回调函数,以便在$digest
循环执行时能够相应的更新视图。也就是说,监听器越多,AngularJS需要处理的回调函数也就越多。
现在假设在视图中有很多值需要被AngularJS处理。比如说像上面使用插值指令来进行数据绑定,虽然我们可能并不想让这个值只绑定一次,比如说上面的Pascal
,在应用代码执行的整个过程中这个属性都不会发生改变,但是AngularJS默认依然会在这个属性上绑定一个监听器和回调函数。因此,在$digest
时,AngularJS依然会去特意的关注
这个值,这实在是有些过头了。
这就是我们需要一次性数据绑定的原因。在AngularJS的文档中,我们可以清楚的了解到这个新特性的作用:
一次性数据绑定表达式可以在数据稳定之后,不需要在
$digest
循环中重计算…
一次性数据绑定的出现解决了前面提到的由监听器太多带来的性能问题。那么我们应该如何使用一次性数据绑定呢?
使用一次性数据绑定非常的简单,我们只需要在表达式之前加上双冒号::
即可。比如,前面我们使用了插值指令将name
属性绑定到了视图中:
<p>Hello {{name}}!</p>
使用一次性数据绑定,我们这样写:
<p>Hello {{::name}}!</p>
在AngularJS 1.3版本中,你可以在任何AngularJS的表达式中使用一次性数据绑定。即使在诸如ng-repeat
这样严重依赖于双向绑定的指令中,你依然可以使用它。在自定义的指令中,你可以在属性中这样使用:
<custom-directive two-way-attribute="::oneWayExpression"></custom-directive>
一次性数据绑定的出现解决了AngularJS中饱受诟病的性能问题,官方版本原生支持也使我们不需要在使用bindonce
这样的第三方模块。从一次性数据绑定出现在原生代码中,我们可以看到AngularJS在不断从社区中吸取好的想法,并及时作出改变。AngularJS 1.3只是一个AngularJS不断进化的一个小阶段,在官方承诺的AngularJS 2.0中,我们可以期待到时它带给我们的惊喜。
本文参考自EXPLORING ANGULAR 1.3 - ONE-TIME BINDINGS,原文地址http://blog.thoughtram.io/angularjs/2014/10/14/exploring-angular-1.3-one-time-bindings.html