译文:英文原文
激动人心的时刻到了,Angular 1.3终于发布了,附带了大量的新功能、bug修复、改进当然也有不足。因为这些新的东西,我们想通过一系列的博文详解主要的功能和改进来让新的版本更快的让大家适应。这是“探索Angular 1.3”系列的第一篇,包含了有史以来最重要的功能:单次绑定(one-time binding)。
等等!Angular的数据绑定不是自动和Ui保持同步么?是的,确实是的,这确实很棒。然而,为了实现数据绑定,Angular需要时刻监听相关的值,这就导致了性能问题,而单次绑定就是为此而生。在我们探究单次绑定之前,来让我们先了解了解Angular中数据绑定(databing)和监控器(watcher)的概念。
为了实现数据绑定,Angular使用watch API来监听作用域(scope)中模型(model)的变化。你的应用代码决定了作用域到底是什么到底从哪里。如果你没有创建子作用域,例如通过ngController指令来连接你的DOM和你实际控制器(controller)代码,你就在和rootScope打交道,正如其字面意义,
然而,在你和作用域打交道的同时,一个叫做监控器(watcher)的来监听变化。观察者通过使用在DOM使用指令来注册。让我们使用插入指令来映射DOM作用域中的模型值。
Hello {{name}}!
这个插入指令为作用域(我们的例子里面是$rootScope
)所属的name
值注册了监控器,以此来将值插入并将其显示到DOM。
在作用域中通过标示符来定义一个属性,并且给他分配值,这样无需进一步的动作,值就会很神奇的现实在DOM。
angular.module('myApp', []) .run(function ($rootScope) { $rootScope.name = "Pascal"; }]);
很好!我们刚才通过一个插入指令将一个模型值和绑定到视图。如果值更改之后,视图就会自动更新。让我们增加一个按钮在被点击时候更新name
的值。
<button ng-click="name = 'Christoph'">Click me!</button>;
点击按钮,就会将字符串Christoph
赋值给name
同时会触发$digest循环,DOM也就是相应自动更新。在特殊的情况下我们只单向(top → down)更新值。然而,譬如input
元素有个一个ngModel
的指令,随着用户输入,input
的value
属性值随之改变,同时这些变化也会映射到实际的模型。
这能够实现是因为当digest循环触发之后,Angular驱动当前作用域及其子作用域中所有的监控器检查所有的模型变化并调用专门的监听函数直到模型值不再变化并且没有任何监控器被触发。一旦diges循环完成执行。浏览器重新渲染DOM反映这些变化。
下面是例子:
See the Pen EIyAi by yijian166 (@yijian166) on CodePen.
现在知道了Angular中数据绑定的工作机制,我们或许会惊讶为什么还需要单次绑定(one-time binding)这个功能。
因为Angular使用监控器来实现数据绑定的本质,当我们使用太多监控器就会带来性能的问题。正如我们所知,监控表达式以及他们的回调监控函数同时注册在作用域,这样Angular才能在$digest
循环的过程中处理他们以此来更新对应的视图。简单来说,注册的监控器越多,Angular需要处理的就越多。
此刻,你想象下在你的视图中有大量的动态值需要被Angular赋值,譬如国际化,这在开发者使用Angular数据绑定来本地化app是一个很常见的场景,甚至当应用的语言在运行不能被改变,只是在初始化的时候设置。在这种场景下视图中的每个字符串都需要被写到作用域中,设置一个监控器以此来一旦下一轮$digest
被触发时候能够得到更新。这将会一个很大开支,特别是当你的语言无需再运行时更改。
这就是单次绑定出现的原因。那么,什么是单次绑定呢?来让我们看看官方文档的说法:
单次表达式(One-time expressions)将会在他们初次稳定也就是在初次digest之后不再被重新计算……
这就解决了我们上面所提到的问题。那么,当我们在使用单次绑定到底是怎么样子的呢?Angular 1.3带来了新的插入指令和表达式以此来告诉Angular这个特殊的插入值应该被只绑定一次。
使用单次绑定我们只需要以::
开始表达式即可。因此如果我们需要在上述的例子中使用单次表达式,我们只需改变:
<p>Hello {{name}}</p>
为
<p>Hello {{::name}}</p>;
这个可以在Angular所有的典型表达式中使用。也就是你可以在ng-repeat
中使用,甚至可以由从内而外建立的双向绑定来暴露属性的指令中使用。从外面你可以给他们设置一个单次表达式:
<custom-directive two-way-attribute="::oneWayExpression"></custom-directive>
来让我们看一个实际的例子。我们已经将name
更新为了::name
来使用单次绑定。下面的代码就证明了可以成功的单次绑定。还记得吧,我们将按钮设置为更新name
为Christioph
,好,让我们试一试:
代码示例:
See the Pen CDmKr by yijian166 (@yijian166) on CodePen.
完美!`name`并没有再次更改。再说`Pascal`是一个更好的名字对吧?