启动期
以下描述解释了一个简单的 Hello World! 应用的启动过程:
-
浏览器加载 HTML 并解析成 DOM
-
浏览器加载
angular.js脚本 -
Angular 等待
DOMContentLoaded事件 -
Angular 寻找
ng-app(指令),这个指令界定了应用程序的作用范围(边界) -
ng-app中指定的Module(如果有的话)被用来配置$injector(注入器) -
$injector用于创建$compile(编译器)服务以及$rootScope(根作用域) -
$compile服务用于编译 DOM 并将其连接至$rootScope -
ng-init(指令)将 "World" 这个字符串赋值给当前作用域下的name属性 -
{{ name }}(插入型表达式)将name属性插入 DOM,生成 "Hello World!"
源代码
<!DOCTYPE html>
<html ng-app>
<head>
<script scr="angular.js"></script>
</head>
<body>
<!-- 这里只是范例,实际上通常都是在 controller 里进行赋值而不是在这里 -->
<p ng-init="name='World'">Hello {{ name }}!</p>
</body>
</html>
运行期
以下内容描述了 Angular 如何与浏览器的事件回圈进行交互:
-
浏览器的
event-loop(事件回圈)等待一个事件的到达;一个事件可能是一次用户交互,也可能是一个计时器,或者是网络事件(比如来自服务器的响应) -
事件的回调函数执行,这将进入 JavaScript 的语境(也就是 context,或称“上下文环境”)。回调函数可以修改 DOM 的结构
-
一旦回调函数执行完毕,浏览器离开 JavaScript 语境,并根据 DOM 的变化重新渲染视图
上图右边显示出 Angular 通过提供自己的事件处理回圈机制来改变 JavaScript 的执行流程,这种机制将 JavaScript 语境分成两部分:传统的执行语境和 Angular 的执行语境。只有应用于 Angular 执行语境的操作才能受益于 Angular 的数据绑定(data-binding),异常处理(Exception handling),属性监视(property watching),等等……也可以通过使用 $apply() 来进入 Angular 的执行语境。
要注意的是在绝大多数地方(控制器,服务),$apply() 已经通过指令(指令用来处理事件)被调用了(简言之,在 Angular 的执行语境下,没有必要显式调用它)。只有在需要执行自定义的事件回调函数,或是在处理第三方库的回调之时才需要显式调用 $apply()。
-
通过调用
scope.$apply (stimulusFn)进入 Angular 的执行语境。stimulusFn既是你想要在 Angular 执行语境下做的一切事情。 -
Angular 执行
stimulusFn(),通常这会改变应用程序的状态 -
Angular 进入
$digest loop,这个回圈本身由两个更小的回圈组成,一个用来处理$evalAsync queue,另一个用来处理$watch list。$digest loop不断地迭代直到模型稳固下来,技术层面上讲就是$evalAsync queue为空并且$watch list没有侦测到任何变化的时刻 -
$evalAsync queue用来安排一些特定的(需要异步执行的)工作,这里特指那些需要出现在当前堆栈帧(current stack frame)以外(执行位置),并在浏览器视图渲染之前(执行时间)的工作。这种机制常常(非 Angular)是用setTimeout(0)来实现的,但是setTimeout(0)要么会变得缓慢,要么会引起视图闪烁(因为浏览器在每一个事件结束之后渲染视图) -
$watch list保存了一组自上次迭代后可能发生了改变的表达式。在侦测到某个变动之时$watch函数即被调用,并且通常会用新值来更新 DOM -
一旦
$digest loop完成,并离开 Angular 和 JavaScript 的执行语境之后,浏览器就紧跟着去重绘 DOM 以响应任何变化
以下是另一个解释,描述了如何实现数据绑定效果(用户在文本框输入文字):
-
在编译期阶段:
-
ng-model和input指令为<input>设置了一个keydown事件监听器 -
{{ name }}插值表达式产生了一个$watch用以响应name的变化
-
-
在运行期阶段:
- 按下 'x' 键会使浏览器触发设置在
<input>上的keydown事件 -
input指令捕捉了这一变化然后调用了$apply("name='x';")来更新应用程序模型(在 Angular 执行语境内) - Angular 将
name = 'x';应用给模型 -
$digest loop开始 -
$watch list侦测到了name属性的变化并且通知{{ name }}插值表达式(令其重新解析),最终这将转变成 DOM 的更新 - Angular 退出自身执行语境,继而退出
keydown事件和相关的 JavaScript 执行语境
- 按下 'x' 键会使浏览器触发设置在
源代码
<!DOCTYPE html>
<html ng-app>
<head>
<script src="angualr.js"></script>
</head>
<body>
<input ng-model="name">
<p>Hello {{ name }}!</p>
</body>
</html>

