AngularJS的启动引导过程

引导之前:库阶段

在示例中,我们定义了一个指令ez-duang, 它应该会展开成一个动画 显示出来。

示例:http://www.hubwiz.com/course/54f3ba65e564e50cfccbad4b/

但是,看起来没有什么动画显示出来。AngularJS似乎没有工作,为什么?

有点像操作系统,AngularJs也有一个启动引导的概念。

当你在HTML文件中引入angular.min.js时,AngularJS只是建立了一个全局的 angular对象,这个对象有一些方法可供开发者调用,但应用的框架还没有建立。

在这个阶段,AngularJS还只是一个库,和jQuery类似,你可以使用angular.element() 操作DOM,也可以使用angular.injector()创建注入器... 但是,你定义的指令,你 创建的控制器,你封装的服务,你开发的模板...所有这些组件,还静静地躺在那里, 没有被整合在一起。

我们说,框架还没有运转起来,现在还是库阶段。

只有通过启动引导,AngularJS框架才开始将那些组件拼接在一起,应用才真正 开始运转。

像下面这样,试着给html元素增加一个ng-app指令,再重新运行!

  1. <html ng-app="ez-stuff">
  2. ....
  3. </html>

自动引导启动框架

就像你看到的那样,如果HTML模板中有某个标签有ng-app属性,那么当DOM树建立成功后, AngularJS就会自动进入引导过程,启动整个框架:

技术分享

试着把ng-app指令挪到body元素上,看看有什么不同?

手工引导启动框架

在大多数情况下,我们都使用ng-app指令来进行自动引导启动,但是如果一个HTML文件中 有多个ng-app,AngularJS只会自动引导启动它找到的第一个ng-app应用,这是需要手工引导 的一个应用场景。

我们可以利用angular.bootstrap()方法进行手动引导:

  1. angular.bootstrap(element, [modules], [config]);

bootstrap方法有三个参数:

  • element : 一个DOM元素,以这个元素为Angular应用的根,等同自动引导时ng-app所在 的元素。这个参数是必须的。比如:document、document.body等。
  • modules : 引导时需要载入的模块数组。比如:[]、["ezstuff"]等。由于我们的HTML中引用 了ezstuff模块中定义的ez-duang指令,所以,我们需要指定载入ezstuff模块。
  • config :引导配置项,可选。我们先忽略。

最终,我们使用如下的形式进行手动引导:

  1. angular.bootstrap(document,["ezstuff"]);

改进的代码已经预置于http://www.hubwiz.com/course/54f3ba65e564e50cfccbad4b/ 中的“AngularJS的启动引导过程”第三页

 

引导第1步:创建注入器

引导过程使AngularJS从库转变成了一个框架。

回忆我们之前提到,AngularJS深入骨髓地使用着依赖注入,那么,在引导过程 之初,首先需要创建一个注入器就毫不奇怪了。

注入器是通向AngularJS所有功能的入口,而AngularJS的功能实现,是通过模块的方式组织的。所以, 在创建注入器的时候,需要告诉AngularJS载入哪些模块(ng模块是内置载入的,不需要显式指定)。

技术分享

在自动启动引导的场景下,可以给ng-app赋值以指定一个需要载入的模块,比如:

  1. ng-app = "ezstuff"

在手动启动引导的场景下,通过bootstrap方法的第二个参数指定需要载入的模块,比如:

  1. angular.bootstrap(document,["ezstuff"]);

INSIDE:无论自动启动还是手工启动,最终都是调用angular对象上的injector()方法创建了一个 注入器,然后把这个注入器存入了根对象的data里:

  1. var injector = angular.injector(["ng","ezstuff"]);
  2. angular.element(document).data("$injector",injector);

我们在http://www.hubwiz.com/course/54f3ba65e564e50cfccbad4b/ 中的“AngularJS的启动引导过程”第四页的代码中,开始模拟引导启动过程的第一步:创建注入器。

引导第2步:创建根作用域

scope对象是AngularJS实现数据绑定的重要服务,所以,在引导启动建立了注入器之后, AngularJS马上在应用的根节点上创建一个根作用域:$rootScope对象。

技术分享

如果是自动引导启动,那么ng-app所在的DOM节点对应着根作用域。如果是手工引导启动, 那么在bootstrap方法中指定的第一个参数就对应着根作用域。

无论哪一种情况,一旦$rootScope对象创建成功,AngularJS就将这个对象存储到根节点 的data中,我们可以使用如下的方法查看这个对象:

  1. angular.element(approot).data("$rootScope");

你可以摆弄一下代码,看看$rootScope到底是什么东西。

引导第3步:编译DOM子树

引导过程的最后一步,是以ng-app所在DOM节点为根节点,对这棵DOM子树进行编译。

技术分享

编译过程通常借助于指令,完成这几种操作:

  1. 对DOM对象进行变换。
  2. 在DOM对象上挂接事件监听。
  3. 在DOM对象对应的scope对象上挂接数据监听。

编译过程是AngularJS相当有特点的一个存在,我们将在下一节继续深入。

现在你应该看到结果了吧?

编译器/$compile

编译器$compile是一个AngularJS的内置服务,它负责遍历DOM树来查找匹配指令, 并调用指令的实现代码进行处理。

HTML编译包括3个步骤:

  • 匹配指令

$compile遍历DOM树,如果发现有元素匹配了某个指令,那么这个指令将被加入 该DOM元素的指令列表中。一个DOM元素可能匹配多个指令。

  • 执行指令的编译函数

当一个DOM元素的所有指令都找齐后,编译器根据指令的优先级/priority指令进行排序。 每个指令的compile函数被依次执行。每个compile执行的结果产生一个link函数,这些 link函数合并成一个复合link函数。

  • 执行生成的链接函数

$compile通过执行指令的link函数,将模板和scope链接起来。结果就是一个DOM视图和scope对象模型 之间的动态数据绑定。

为何将编译和连接两个步骤分开?

简单说,当数据模型的变化会导致DOM结构变化时,指令就需要分别定义compile()函数和link函数。 例如,ng-repeat指令需要为数据集合中的每个成员复制DOM元素。将编译和链接过程分开可以有效 地提高性能,因为DOM的复制放在compile()里,仅需要执行一次,但链接则发生在每个生成的DOM元素 上,所以指令的link()函数会执行多次。

指令很少需要compile函数,因为大多数指令考虑的是作用于特定的DOM元素实例,而不是改变DOM 的结构。所以link函数更常用。

指令/directive

笼统地说,指令是DOM元素(例如属性、元素、CSS类等)上的标记符,用来告诉AngularJS的HTML编译器 ($compile服务)将特定的行为绑定到DOM元素,或者改变DOM元素。

指令可以放置在元素名、属性、CSS类名称及备注中。下面是一些等效的触发"ng-bind"指令的写法:

  1. <span ng-bind="exp"></span>
  2. <span class="ng-bind: exp;"></span>
  3. <ng-bind></ng-bind>
  4. <!-- directive: ng-bind exp -->

指令的实现本质上就是一个类工厂,它返回一个指令定义对象,编译器根据这个指令定义对象进行操作。

技术分享

问题是,HTML中的ez-duang,怎么就匹配到了JavaScript中的ezDuang?

指令的规范化

AngularJS在进行匹配检测之前,首先对HTML元素的标签和属性名转化成规范的驼峰式字符串:

  1. 去除名称前缀的x-和data-
  2. 以: , - 或 _ 为分割符,将字符串切分成单词,除第一个单词外,其余单词首字母大写
  3. 重新拼接各单词

例如,下面的写法都等效地匹配ngBind指令:

  1. <span ng-bind="name"></span> <br>
  2. <span ng:bind="name"></span> <br>
  3. <span ng_bind="name"></span> <br>
  4. <span data-ng-bind="name"></span> <br>
  5. <span x-ng-bind="name"></span> <br>

所以,在前面的课程中,我们在HTML中使用的ez-duang指令,将被规范为ezDuang, 编译器使用这个规范化的名称与注册的指令进行匹配。

参考资料:http://www.hubwiz.com/course/54f3ba65e564e50cfccbad4b/ 

郑重声明:本站内容如果来自互联网及其他传播媒体,其版权均属原媒体及文章作者所有。转载目的在于传递更多信息及用于网络分享,并不代表本站赞同其观点和对其真实性负责,也不构成任何其他建议。