angular 双向数据绑定原理

要理解angular双向数据绑定,首先要理解js的事件轮询---event-loop;html

JavaScript 运行机制详解:再谈Event Loop 这篇文章介绍的不错;
ajax

理解了以上内容以后就能够看下面这个文章;
api

angular何为做用域(Scope)
浏览器

与浏览器事件轮循整合

下图与示例描述了Angular如何与浏览器事件轮循进行交互。网络

  1. 浏览器的事件轮循等待事件到来,事件能够是用户交互,定时器事件,或是网络事件(如 ajax 返回)app

  2. 事件发生,其回调被执行,回调的执行就使得应用程序的执行上下文进入到了 JavaScript 的上下文。而后在 JavaScript的上下文中执行,并修改相关的DOM结构异步

  3. 一旦回调执行完毕,浏览器就离开 JavaScript的上下文回到浏览器上下文并基于DOM结构的改变从新渲染视图async

讲了那么多些,那么Angular是怎么在这里横插一杠呢?看图,Angular是插进了 JavaScript的上下文中,经过提供Angular本身的事件处理轮循来改变正常的JavaScript工做流。它实际上是把JavaScript上下文很成了两块:一个是传统的JavaScript执行上下文(图中浅蓝色区域),一个是Angular的执行上下文(图中淡黄色区域)。 只有在Angular上下文执行的操做才会受益于Angular的数据绑定,异常处理,属性检测,等等。固然,若是不在Angular的上下文中,你也可使用 $apply() 来进入Angular的执行上下文。 须要注意的是,$apply() 在Angular自己的不少地方(如控制器,服务等)都已经被隐式地调用了来处理事件轮循。 显示地使用 $apply() 只有在你从 JavaScript上下文或是从第三方类库的回调中想要进入Angular时才须要。让咱们来看看具体的流程:ide

  1. 进入Angular执行上下文的方法,调用 scope.$apply(stimulusFn) 。上面 $apply() 中的参数 stimulusFn 是你想要让它进入Angular上下文的代码函数

  2. 进入 $apply() 以后,Angular执行 stimulusFn() ,而这个函数一般会改变应用程序的状态(多是数据,或是方法调用等)

  3. 以后,Angular进入 $digest 轮循。这个轮循是由两个较小的轮循构成,一个是处理 $evalAsync 队列(异步计算的队列),另外一个是处理 $watch 列表。 $digest 轮循不断迭代变动(在 $eval 和 $watch 之间变动)直到数据模型稳定,这个状态其实就是evalAsync 队列为空且$watch 列表再也不监测到变化为止。(译注:其实这里就是全部外来的异步操做堆起来成为一个队列,由$eval一个个计算,而后 $watch 看一下这个异步操做对应的数据模型是否还有改变,有改变,就继续 $eval 这个异步操做,若是没改变,那就拿异步操做队列里的下个异步操做重复上述步骤,直到异步操做队列为空以及 $watch 再也不监测到任何数据模型变化为止)

  4. $evalAsync 队列是用来安排那些待进入Angular$digest 的异步操做,这些操做每每是在浏览器的视图渲染以前,且经常是经过setTimeout(0) 触发。可是用 setTimeout(0) 这个方法就不得不承受缓慢迟钝的响应以及可能引发的闪屏(由于浏览器在每次事件发生后都会渲染一次)(译注:这里我的以为不要理解的太复杂,按照上面第三点理解就够用了,这边我的翻译的也不是太好,后期配以例子完善)

  5. $watch 列表则是存放了一组通过 $eval 迭代以后可能会改变的Angular的表达式集合。若是数据模型变化被监测到,那么 $watch 函数被调用进而用新值更新DOM。

  6. 一旦Angular的 $digest 轮循完成,那么应用程序的执行就会离开Angular及 JavaScript的上下文。而后浏览器从新渲染DOM来反映发生的变化

接下来是传统的 Hello world 示例(就是本节的第一个例子)的流程剖析,这样你应该就能明白整个例子是如何在用户输入时产生双向绑定的。

  1. 编译阶段:

    1. ng-model 和 input 指令 在 <input> 标签中设置了一个 keydown 监听器

    2. {{greeting}} 插值(也就是表达式)这里设置了一个 $watch 来监测 username 的变化

  2. 执行阶段:

    1. 在 <input> 输入框中按下 'X' 键引发浏览器发出一个 keydown 事件

    2. input 指令捕捉到输入值的改变调用 $apply("username = 'X';") 进入Angular的执行环境来更新应用的数据模型

    3. Angular将 username = 'X'; 做用在数据模型之上,这样 scope.username 就被赋值为 'X' 了

    4. $digest 轮循开始

    5. $watch 列表中监测到 username 有一个变化,而后通知 {{greeting}} 插值表达式,进而更新DOM

    6. 执行离开Angular的上下文,进而 keydown 事件结束,而后执行也就退出了 JavaScript的上下文;这样 $digest 完成

    7. 浏览器用更新了的值从新渲染视图

相关文章
相关标签/搜索