理解Angular数据双向绑定

AngularJS是一款优秀的前端JS框架,已经被用于Google的多款产品当中。AngularJS有着诸多特性,最为核心的是:MVVM、模块化、自动化双向数据绑定、语义化标签、依赖注入等等。

一.什么是数据双向绑定

Angular实现了双向绑定机制。所谓的双向绑定,无非是从界面的操作能实时反映到数据,数据的变更能实时展现到界面。

一个最简单的示例就是这样:

function CounterCtrl($scope) { $scope.counter = 1; }

这个例子很简单,每当点击一次按钮,界面上的数字就增加一。

二.数据双向绑定原理

1.深入理解 实现用户控制手机列表显示顺序的特性。动态排序可以这样实现,添加一个新的模型属性,把它和迭代器集成起来,然后让数据绑定完成剩下的事情。

模板(app/index.html)

在index.html中做了如下更改:

首先,增加了一个叫做orderProp的

这样输入值会自动映射到user对象的name属性,反之亦然。到此这个简单实现就完成了。

四.Angular实现数据双向绑定

Angular主要通过scopes实现数据双向绑定。AngularJS的scopes包括以下四个主要部分:

digest循环以及dirty-checking,包括watch,digest,和$apply。 Scope继承 - 这项机制使得我们可以创建scope继承来分享数据和事件。 对集合 – 数组和对象 – 的有效dirty-checking。 事件系统 - on,emit,以及$broadcast。

我们主要讲解第一条Angular数据绑定是怎么实现的。

1.digest循环以及dirty-checking,包括watch,和$apply ①浏览器事件循环和Angular.js扩展 我们的浏览器一直在等待事件,比如用户交互。假如你点击一个按钮或者在输入框里输入东西,事件的回调函数就会在javascript解释器里执行,然后你就可以做任何DOM操作,等回调函数执行完毕时,浏览器就会相应地对DOM做出变化。 Angular拓展了这个事件循环,生成一个有时成为angular context的执行环境(这是个重要的概念)。

②watch队列(watch list) 每次你绑定一些东西到你的UI上时你就会往$watch队列里插入一条$watch。想象一下$watch就是那个可以检测它监视的model里时候有变化的东西。

当我们的模版加载完毕时,也就是在linking阶段(Angular分为compile阶段和linking阶段---译者注),Angular解释器会寻找每个directive,然后生成每个需要的$watch。

③$digest循环 还记得我前面提到的扩展的事件循环吗?当浏览器接收到可以被angular context处理的事件时,digest循环就会触发。这个循环是由两个更小的循环组合起来的。一个处理evalAsync队列,另一个处理watch队列。 这个是处理什么的呢?digest将会遍历我们的watch,然后询问它是否有属性和值的变化,直$watch队列都检查过。

这就是所谓的dirty-checking。既然所有的$watch都检查完了,那就要问了:有没有$watch更新过?如果有至少一个更新过,这个循环就会再次触发,直到所有的$watch都没有变化。这样就能够保证每个model都已经不会再变化。记住如果循环超过10次的话,它将会抛出一个异常,防止无限循环。 当$digest循环结束时,DOM相应地变化。

例如:controllers.js

$scope.changeFoo = function() {
$scope.name = "Bar";
}
});

这里我们有一个$watch因为ng-click不生成$watch(函数是不会变的)。

  • 查询每个$watch是否变化。

这里很重要的(也是许多人的很蛋疼的地方)是每一个进入angular context的事件都会执行一个$digest循环,也就是说每次我们输入一个字母循环都会检查整个页面的所有$watch。

④通过$apply来进入angular context 谁决定什么事件进入angular context,而哪些又不进入呢?$apply!

如果当事件触发时,你调用apply,它会进入angularcontext,如果没有调用就不会进入。现在你可能会问:刚才的例子里我也没有调用apply,为什么?Angular为你做了!因此你点击带有ng-click的元素时,时间就会被封装到一个apply调用。如果你有一个ng−model="foo"的输入框,然后你敲一个f,事件就会这样调用apply("foo = 'f';")。

Angular什么时候不会自动为我们apply呢?这是Angular新手共同的痛处。为什么我的jQuery不会更新我绑定的东西呢?因为jQuery没有调用apply,事件没有进入angular context,$digest循环永远没有执行。

2.具体实现 AngularJS的scopes就是一般的JavaScript对象,在它上面你可以绑定你喜欢的属性和其他对象,然而,它们同时也被添加了一些功能用于观察数据结构上的变化。这些观察的功能都由dirty-checking来实现并且都在一个digest循环中被执行。 ①Scope 对象 创建一个test/scope_spec.js文件,并将下面的测试代码添加到其中:

这个测试用来创建一个Scope,并在它上面赋一个任意值。我们可以轻松的让这个测试通过:创建src/scope.js文件然后在其中添加以下内容: src/scope.js

在这个测试中,我们将一个属性(aProperty)赋值给了这个scope。这正是Scope上的属性运行的方式。它们就是正常的JavaScript属性,并没有什么特别之处。这里你完全不需要去调用一个特别的setter,也不需要对你赋值的类型进行什么限制。真正的魔法在于两个特别的函数:watch和digest。我们现在就来看看这两个函数

②监视对象属性:watch和digest watch和digest是同一个硬币的两面。它们二者同时形成了$digest循环的核心:对数据的变化做出反应。

为了实现这一块功能,我们首先来定义一个测试文件并断言你可以使用watch来注册一个监视器,并且当有人调用了digest的时候监视器的监听函数会被调用

在scope_spec.js文件添加一个嵌套的describe块。并创建一个beforeEach函数来初始化这个scope,以便我们可以在进行每个测试时重复它:

test/scope_spec.js

在上面的这个测试中我们调用了watch来在这个scope上注册一个监视器。我们现在对于监视函数本身并没有什么兴趣,因此我们随便提供了一个函数来返回一个常数值。作为监听函数,我们提供了一个JasmineSpy。接着我们调用了digest并检查这个监听器是否真正被调用

首先,这个Scope需要有一些地方去存储所有被注册的监视器。我们现在就在Scope构造函数添加一个数组存储它们:

src/scope.js

function Scope(){
this.$$watchers = [];
}

上面代码中的$$前缀在AngularJS框架中被认为是私有变量,它们不应该在应用的外部被调用。 现在我们可以来定义watch函数了。它接收两个函数作为参数,并且将它们储存在$watchers数组中。我们想要每一个Scope对象都拥有这个函数,因此我们将它添加到Scope的原型中:

最后我们应该有一个digest函数。现在,我们来定义一个digest函数的简化版本,它仅仅只是会迭代所有的注册监视器并调用它们的监听函数: digest能够持续的迭代所有监视函数,直到被监视的值停止变化。多做几次digest是我们能够获得运用于监视器并依赖于其他监视器的变化。

首先,我们新建名为$$digestOnce,并且调整它以便它能够在所有监视器上运行一遍,然后返回一个布尔值来说明有没有任何变化: src/scope.js

Scope.prototype.$$digestOnce = function(){
var length = this.$$watchers.length;
var watcher,newValue,oldValue,dirty;
while(length--){
watcher = this.$$watchers[length];
newValue = watcher.watchFn(this);
oldValue= watcher.last;
if(newValue !== oldValue){
watcher.last == newValue;
watcher.listenerFn(newValue,this);
dirty = true;
}
}
return dirty;
};

接着,我们重定义digest以便它能够运行“外循环”,在变化发生时调用$digestOnce:

src/scope.js

以上就是Angular数据双向绑定的相关介绍,希望对大家的学习有所帮助。

相关文章

事件冒泡和事件捕获 起因:今天在封装一个bind函数的时候,发现el.addEventListener函数支持第三个参数...
js小数运算会出现精度问题 js number类型 JS 数字类型只有number类型,number类型相当于其他强类型语言...
什么是跨域 跨域 : 广义的跨域包含一下内容 : 1.资源跳转(链接跳转,重定向跳转,表单提交) 2.资源...
@ "TOC" 常见对base64的认知(不完全正确) 首先对base64常见的认知,也是须知的必须有...
搞懂:MVVM模式和Vue中的MVVM模式 MVVM MVVM : 的缩写,说都能直接说出来 :模型, :视图, :视图模...
首先我们需要一个html代码的框架如下: 我们的目的是实现ul中的内容进行横向的一点一点滚动。ul中的内容...