要理解angular双向数据绑定,首先要理解js的事件轮询---event-loop;
JavaScript 运行机制详解:再谈Event Loop 这篇文章介绍的不错;
理解了以上内容之后就可以看下面这个文章;
与浏览器事件轮循整合
下图与示例描述了Angular如何与浏览器事件轮循进行交互。
浏览器的事件轮循等待事件到来,事件可以是用户交互,定时器事件,或是网络事件(如 ajax 返回)
事件发生,其回调被执行,回调的执行就使得应用程序的执行上下文进入到了 JavaScript 的上下文。然后在 JavaScript的上下文中执行,并修改相关的DOM结构
一旦回调执行完毕,浏览器就离开 JavaScript的上下文回到浏览器上下文并基于DOM结构的改变重新渲染视图
讲了那么多些,那么Angular是怎么在这里横插一杠呢?看图,Angular是插进了 JavaScript的上下文中,通过提供Angular自己的事件处理轮循来改变正常的JavaScript工作流。它其实是把JavaScript上下文很成了两块:一个是传统的JavaScript执行上下文(图中浅蓝色区域),一个是Angular的执行上下文(图中淡黄色区域)。 只有在Angular上下文执行的操作才会受益于Angular的数据绑定,异常处理,属性检测,等等。当然,如果不在Angular的上下文中,你也可以使用 $apply() 来进入Angular的执行上下文。 需要注意的是,$apply() 在Angular本身的很多地方(如控制器,服务等)都已经被隐式地调用了来处理事件轮循。 显示地使用 $apply() 只有在你从 JavaScript上下文或是从第三方类库的回调中想要进入Angular时才需要。让我们来看看具体的流程:
进入Angular执行上下文的方法,调用 scope
.$apply(stimulusFn)。上面$apply()中的参数stimulusFn是你想要让它进入Angular上下文的代码进入
$apply()之后,Angular执行stimulusFn(),而这个函数通常会改变应用程序的状态(可能是数据,或是方法调用等)之后,Angular进入
$digest轮循。这个轮循是由两个较小的轮循构成,一个是处理$evalAsync队列(异步计算的队列),另一个是处理$watch列表。$digest轮循不断迭代变更(在$eval和$watch之间变更)直到数据模型稳定,这个状态其实就是evalAsync队列为空且$watch列表不再监测到变化为止。(译注:其实这里就是所有外来的异步操作堆起来成为一个队列,由$eval一个个计算,然后$watch看一下这个异步操作对应的数据模型是否还有改变,有改变,就继续$eval这个异步操作,如果没改变,那就拿异步操作队列里的下个异步操作重复上述步骤,直到异步操作队列为空以及$watch不再监测到任何数据模型变化为止)$evalAsync队列是用来安排那些待进入Angular$digest的异步操作,这些操作往往是在浏览器的视图渲染之前,且常常是通过setTimeout(0)触发。但是用setTimeout(0)这个方法就不得不承受缓慢迟钝的响应以及可能引起的闪屏(因为浏览器在每次事件发生后都会渲染一次)(译注:这里个人觉得不要理解的太复杂,按照上面第三点理解就够用了,这边个人翻译的也不是太好,后期配以例子完善)$watch列表则是存放了一组经过$eval迭代之后可能会改变的Angular的表达式集合。如果数据模型变化被监测到,那么$watch函数被调用进而用新值更新DOM。一旦Angular的
$digest轮循完成,那么应用程序的执行就会离开Angular及 JavaScript的上下文。然后浏览器重新渲染DOM来反映发生的变化
接下来是传统的 Hello world 示例(就是本节的第一个例子)的流程剖析,这样你应该就能明白整个例子是如何在用户输入时产生双向绑定的。
-
编译阶段:
在
{{greeting}}插值(也就是表达式)这里设置了一个$watch来监测username的变化
-
执行阶段:
在
<input>输入框中按下 'X' 键引起浏览器发出一个keydown事件input指令捕捉到输入值的改变调用$apply("username = 'X';")进入Angular的执行环境来更新应用的数据模型Angular将
username = 'X';作用在数据模型之上,这样scope.username就被赋值为'X'了$digest轮循开始$watch列表中监测到username有一个变化,然后通知{{greeting}}插值表达式,进而更新DOM执行离开Angular的上下文,进而
keydown事件结束,然后执行也就退出了 JavaScript的上下文;这样$digest完成浏览器用更新了的值重新渲染视图
转载于:https://my.oschina.net/u/782865/blog/513188