angular 双向数据绑定原理
要理解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