Angular学习笔记(16)—揭秘Angular

当浏览器触发DOMContentLoaded事件时,Angular就开始工作。它首先寻找ng-app指令。如果浏览器在DOM中找到ng-app指令,它会为我们自动启动应用。如果没有找到这个指令,Angular期望我们自己手动启动应用。
要手动启动一个AngularJS应用,可以使用Angular的bootstrap()方法。在一些罕见的情况下手动启用应用程序是有意义的。例如,想要在某个其他库的代码运行之后,或者在运行时动态创建元素时,启动AngularJS应用。要想手动启动应用,可以像下面这样启动它:

var newElement = document.createElement("div");
angular.bootstrap(newElement, ['myApp']);

如果在DOM中没有找到ng-app指令,而且也没有手动启动应用,则AngularJS不会运行。忘记在页面中引入ng-app指令肯定会引发一些严重的问题。
bootstrap()方法只允许我们启动angular应用一次。
如果在ng-app属性中没有指定应用程序,则Angular会加载一个不带特定模块的应用。如果指定了,Angular就会加载与这个指令关联的模块。
一旦应用程序加载完成,$injector就会在应用程序的$rootScope旁边创建$compile服务。
$rootScope创建后,$compile服务就会接管它。它会将$rootScope与现有的DOM连接起来,然后从将ng-app指令设置为祖先的地方开始编译DOM。

视图的工作原理

当浏览器在标准的Web流程中获取HTML时,它会收到HTML代码并将它解析为一个DOM树。这个DOM树中的每个元素被称作DOM元素,这些DOM元素会构建一堆节点。然后浏览器负责绘制出这个DOM树的结构。
浏览器在提取脚本时(从<script>标签中),会暂停DOM解析并等待脚本取回(你可以修改这一行为,但是这个行为是默认的)。
angular.js被取回时,浏览器会执行它,同时设置一个事件监听器来监听浏览器的DOMContentLoaded事件。DOMContentLoaded事件会在HTML文档加载完成并开始解析时触发。
检测到这个事件时,Angular会查找ng-app指令,然后创建运行需要的一系列必要的组件(即$injector$compile服务以及$rootScope),然后开始解析DOM树。

编译阶段

$compile服务会遍历DOM树并搜集它找到的所有指令,然后将所有这些指令的链接函数合并为一个单一的链接函数。
然后这个链接函数会将编译好的模板链接到$rootScope中(也就是附属于ng-app所在的DOM元素的作用域)。
它会通过检查DOM属性、注释、类以及DOM元素名称的方式查找指令。
$compile服务通过遍历DOM树的方式查找有声明指令的DOM元素。当碰到带有一个或多个指令的DOM元素时,它会排序这些指令(基于指令的priority优先级),然后使用$injector服务查找和收集指令的compile函数并执行它。
指令中的compile函数会在适当的时候处理所有DOM转换或者内联模板,如同创建模板的副本。

// 返回一个链接函数
var linkFunction = $compile(appElement);
// 调用链接函数,将$rootScope附加给DOM元素
linkFunction($rootScope);

每个节点的编译方法运行之后,$compile服务就会调用链接函数。这个链接函数为绑定了封闭作用域的指令设置监控。这一行为会创建实时视图。
最后,在$compile服务完成后,AngularJS运行时就准备好了。

运行时

在标准的浏览器流程中,事件循环会等待事件执行(比如鼠标移动、点击、按键等)。当这些事件发生时,它们会被放到浏览器的事件队列中。如果有函数处理程序对事件作出响应,浏览器就会将event对象作为参数来调用这些事件处理程序。

ele.addEventListener('click', function(event) {});

Angular中对事件循环做了一点增强,并且Angular还提供了自己的事件循环。指令自身会注册事件监听器,因此当事件被触发时,指令函数就会运行在AngularJS的$digest循环中。
Angular的事件循环被称作$digest循环。这个$digest循环由两个小型的循环组成,分别是evalAsync循环和$watch列表。
当事件被触发时,事件处理程序就会在指令的上下文中进行调用,也就是AngularJS的上下文中。从功能上讲,AngularJS会在包含作用域的$apply()方法内调用指令。Angular是在$rootScope上启动$digest循环时开始整个过程的,并且还会传播到所有子作用域中。
Angular进入$digest循环时,会等待$evalAsync队列清空,然后再将回调执行上下文交还给浏览器。这个$evalAsync用于在浏览器进行渲染之前,调度需要运行在当前桢栈之外的所有任务。
此外,$digest循环还会等待$watch表达式列表,它是一个可能在上一次迭代过程中被改变的潜在的表达式数组。如果检测到变化,就调用$watch函数,然后再次查看$watch列表以确保没有东西被改变。
注意,对于$watch列表中检测到的任何变化,AngularJS都会再次查看这个列表以确保没有东西被改变。
一旦$digest循环稳定下来,并且检测到没有潜在的变化了,执行过程就会离开Angular上下文并且通常会回到浏览器中,DOM将会被渲染到这里。
整个流程在每个浏览器事件之间都会发生,这也是Angular如此强大的原因。它还可以将来自浏览器的事件注入到AngularJS流程中。

©著作权归作者所有,转载或内容合作请联系作者
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

推荐阅读更多精彩内容