[TOC]
# 1.引導(dǎo)之前:庫(kù)階段
在示例中,我們定義了一個(gè)指令ez-duang, 它應(yīng)該會(huì)展開(kāi)成一個(gè)動(dòng)畫 顯示出來(lái)。 AngularJS代碼:
~~~
angular.module("ezstuff",[])
.directive("ezDuang",function(){
return {
restrict : "E",
template : "<img src='http://ww4.sinaimg.cn/bmiddle/757eb2ffjw1eptcr4qobjg209205dthh.gif'>"
};
});
~~~
應(yīng)該在下面看到一幅動(dòng)畫才對(duì)!
但是,看起來(lái)沒(méi)有什么動(dòng)畫顯示出來(lái)。AngularJS似乎沒(méi)有工作,為什么?
有點(diǎn)像操作系統(tǒng),AngularJs也有一個(gè)啟動(dòng)引導(dǎo)的概念。
當(dāng)你在HTML文件中引入angular.min.js時(shí),AngularJS只是建立了一個(gè)全局的 angular對(duì)象,這個(gè)對(duì)象有一些方法可供開(kāi)發(fā)者調(diào)用,但應(yīng)用的框架還沒(méi)有建立。
在這個(gè)階段,AngularJS還只是一個(gè)庫(kù),和jQuery類似,你可以使用angular.element() 操作DOM,也可以使用angular.injector()創(chuàng)建注入器... 但是,你定義的指令,你 創(chuàng)建的控制器,你封裝的服務(wù),你開(kāi)發(fā)的模板...所有這些組件,還靜靜地躺在那里, 沒(méi)有被整合在一起。
我們說(shuō),框架還沒(méi)有運(yùn)轉(zhuǎn)起來(lái),現(xiàn)在還是庫(kù)階段。
只有通過(guò)啟動(dòng)引導(dǎo),AngularJS框架才開(kāi)始將那些組件拼接在一起,應(yīng)用才真正 開(kāi)始運(yùn)轉(zhuǎn)。
像下面這樣,試著給html元素增加一個(gè)ng-app指令,再重新運(yùn)行!
~~~
<html ng-app="ezstuff">
....
</html>
~~~
# 2.自動(dòng)引導(dǎo)啟動(dòng)框架
就像你看到的那樣,如果HTML模板中有某個(gè)標(biāo)簽有ng-app屬性,那么當(dāng)DOM樹(shù)建立成功后, AngularJS就會(huì)自動(dòng)進(jìn)入引導(dǎo)過(guò)程,啟動(dòng)整個(gè)框架:

試著把ng-app指令挪到body元素上,看看有什么不同?
# 3.手工引導(dǎo)啟動(dòng)框架
在大多數(shù)情況下,我們都使用ng-app指令來(lái)進(jìn)行自動(dòng)引導(dǎo)啟動(dòng),但是如果一個(gè)HTML文件中 有多個(gè)ng-app,AngularJS只會(huì)自動(dòng)引導(dǎo)啟動(dòng)它找到的第一個(gè)ng-app應(yīng)用,這是需要手工引導(dǎo) 的一個(gè)應(yīng)用場(chǎng)景。
我們可以利用 angular.bootstrap() 方法進(jìn)行手動(dòng)引導(dǎo):
angular.bootstrap(element, [modules], [config]);
bootstrap方法有三個(gè)參數(shù):
1. element : 一個(gè)DOM元素,以這個(gè)元素為Angular應(yīng)用的根,等同自動(dòng)引導(dǎo)時(shí)ng-app所在 的元素。這個(gè)參數(shù)是必須的。比如:document、document.body等。
2. modules : 引導(dǎo)時(shí)需要載入的模塊數(shù)組。比如:[]、["ezstuff"]等。由于我們的HTML中引用 了ezstuff模塊中定義的ez-duang指令,所以,我們需要指定載入ezstuff模塊。
3. config :引導(dǎo)配置項(xiàng),可選。我們先忽略。
最終,我們使用如下的形式進(jìn)行手動(dòng)引導(dǎo):
`angular.bootstrap(document,["ezstuff"]);` 改進(jìn)的代碼已經(jīng)預(yù)置于在線課程。請(qǐng)點(diǎn)擊【手動(dòng)引導(dǎo)】按鈕啟動(dòng)引導(dǎo)過(guò)程!
# 4.引導(dǎo)第1步:創(chuàng)建注入器
引導(dǎo)過(guò)程使AngularJS從庫(kù)轉(zhuǎn)變成了一個(gè)框架。
回憶我們之前提到,AngularJS深入骨髓地使用著依賴注入,那么,在引導(dǎo)過(guò)程 之初,首先需要?jiǎng)?chuàng)建一個(gè)注入器就毫不奇怪了。
注入器是通向AngularJS所有功能的入口,而AngularJS的功能實(shí)現(xiàn),是通過(guò)模塊的方式組織的。所以, 在創(chuàng)建注入器的時(shí)候,需要告訴AngularJS載入哪些模塊(ng模塊是內(nèi)置載入的,不需要顯式指定)。

在自動(dòng)啟動(dòng)引導(dǎo)的場(chǎng)景下,可以給ng-app賦值以指定一個(gè)需要載入的模塊,比如: ng-app = "ezstuff"
在手動(dòng)啟動(dòng)引導(dǎo)的場(chǎng)景下,通過(guò)bootstrap方法的第二個(gè)參數(shù)指定需要載入的模塊,比如: angular.bootstrap(document,["ezstuff"]);
INSIDE:無(wú)論自動(dòng)啟動(dòng)還是手工啟動(dòng),最終都是調(diào)用angular對(duì)象上的injector()方法創(chuàng)建了一個(gè) 注入器,然后把這個(gè)注入器存入了根對(duì)象的data里:
~~~
var injector = angular.injector(["ng","ezstuff"]);
angular.element(document).data("$injector",injector);
~~~
# 5.引導(dǎo)第2步:創(chuàng)建根作用域
scope對(duì)象是AngularJS實(shí)現(xiàn)數(shù)據(jù)綁定的重要服務(wù),所以,在引導(dǎo)啟動(dòng)建立了注入器之后, AngularJS馬上在應(yīng)用的根節(jié)點(diǎn)上創(chuàng)建一個(gè)根作用域:$rootScope對(duì)象。

如果是自動(dòng)引導(dǎo)啟動(dòng),那么ng-app所在的DOM節(jié)點(diǎn)對(duì)應(yīng)著根作用域。如果是手工引導(dǎo)啟動(dòng), 那么在bootstrap方法中指定的第一個(gè)參數(shù)就對(duì)應(yīng)著根作用域。
無(wú)論哪一種情況,一旦$rootScope對(duì)象創(chuàng)建成功,AngularJS就將這個(gè)對(duì)象存儲(chǔ)到根節(jié)點(diǎn) 的data中,我們可以使用如下的方法查看這個(gè)對(duì)象: angular.element(approot).data("$rootScope");
你可以擺弄一下代碼,看看$rootScope到底是什么東西。
# 6.引導(dǎo)第3步:編譯DOM子樹(shù)
引導(dǎo)過(guò)程的最后一步,是以ng-app所在DOM節(jié)點(diǎn)為根節(jié)點(diǎn),對(duì)這棵DOM子樹(shù)進(jìn)行編譯。

編譯過(guò)程通常借助于指令,完成這幾種操作:
對(duì)DOM對(duì)象進(jìn)行變換。
在DOM對(duì)象上掛接事件監(jiān)聽(tīng)。
在DOM對(duì)象對(duì)應(yīng)的scope對(duì)象上掛接數(shù)據(jù)監(jiān)聽(tīng)。 編譯過(guò)程是AngularJS相當(dāng)有特點(diǎn)的一個(gè)存在,我們將在下一節(jié)繼續(xù)深入。
現(xiàn)在你應(yīng)該看到結(jié)果了吧?
# 7.編譯器/$compile
編譯器$compile是一個(gè)AngularJS的內(nèi)置服務(wù),它負(fù)責(zé)遍歷DOM樹(shù)來(lái)查找匹配指令, 并調(diào)用指令的實(shí)現(xiàn)代碼進(jìn)行處理。
HTML編譯包括3個(gè)步驟:
1. **匹配指令** $compile遍歷DOM樹(shù),如果發(fā)現(xiàn)有元素匹配了某個(gè)指令,那么這個(gè)指令將被加入 該DOM元素的指令列表中。一個(gè)DOM元素可能匹配多個(gè)指令。
2. **執(zhí)行指令的編譯函數(shù)** 當(dāng)一個(gè)DOM元素的所有指令都找齊后,編譯器根據(jù)指令的優(yōu)先級(jí)/priority指令進(jìn)行排序。 每個(gè)指令的compile函數(shù)被依次執(zhí)行。每個(gè)compile執(zhí)行的結(jié)果產(chǎn)生一個(gè)link函數(shù),這些 link函數(shù)合并成一個(gè)復(fù)合link函數(shù)。
3. **執(zhí)行生成的鏈接函數(shù)** $compile通過(guò)執(zhí)行指令的link函數(shù),將模板和scope鏈接起來(lái)。結(jié)果就是一個(gè)DOM視圖和scope對(duì)象模型 之間的動(dòng)態(tài)數(shù)據(jù)綁定。
## 為何將編譯和連接兩個(gè)步驟分開(kāi)?
簡(jiǎn)單說(shuō),當(dāng)數(shù)據(jù)模型的變化會(huì)導(dǎo)致DOM結(jié)構(gòu)變化時(shí),指令就需要分別定義compile()函數(shù)和link函數(shù)。 例如,ng-repeat指令需要為數(shù)據(jù)集合中的每個(gè)成員復(fù)制DOM元素。將編譯和鏈接過(guò)程分開(kāi)可以有效 地提高性能,因?yàn)镈OM的復(fù)制放在compile()里,僅需要執(zhí)行一次,但鏈接則發(fā)生在每個(gè)生成的DOM元素 上,所以指令的link()函數(shù)會(huì)執(zhí)行多次。
指令很少需要compile函數(shù),因?yàn)榇蠖鄶?shù)指令考慮的是作用于特定的DOM元素實(shí)例,而不是改變DOM 的結(jié)構(gòu)。所以link函數(shù)更常用。
# 8.指令/directive
籠統(tǒng)地說(shuō),指令是DOM元素(例如屬性、元素、CSS類等)上的標(biāo)記符,用來(lái)告訴AngularJS的HTML編譯器 ($compile服務(wù))將特定的行為綁定到DOM元素,或者改變DOM元素。
指令可以放置在元素名、屬性、CSS類名稱及備注中。下面是一些等效的觸發(fā)"ng-bind"指令的寫法:
~~~
<span ng-bind="exp"></span>
<span class="ng-bind: exp;"></span>
<ng-bind></ng-bind>
<!-- directive: ng-bind exp -->
~~~
指令的實(shí)現(xiàn)本質(zhì)上就是一個(gè)類工廠,它返回一個(gè)指令定義對(duì)象,編譯器根據(jù)這個(gè)指令定義對(duì)象進(jìn)行操作。

問(wèn)題是,HTML中的ez-duang,怎么就匹配到了JavaScript中的ezDuang?
9.指令的規(guī)范化
AngularJS在進(jìn)行匹配檢測(cè)之前,首先對(duì)HTML元素的標(biāo)簽和屬性名轉(zhuǎn)化成規(guī)范的駝峰式字符串:
1. 去除名稱前綴的x-和data-
2. 以: , - 或 _ 為分割符,將字符串切分成單詞,除第一個(gè)單詞外,其余單詞首字母大寫
3. 重新拼接各單詞 例如,下面的寫法都等效地匹配ngBind指令:
~~~
<span ng-bind="name"></span> <br>
<span ng:bind="name"></span> <br>
<span ng_bind="name"></span> <br>
<span data-ng-bind="name"></span> <br>
<span x-ng-bind="name"></span> <br>
~~~
所以,在前面的課程中,我們?cè)贖TML中使用的`ez-duang`指令,將被規(guī)范為ezDuang, 編譯器使用這個(gè)規(guī)范化的名稱與注冊(cè)的指令進(jìn)行匹配。
- 步入JavaScript的世界
- 二進(jìn)制運(yùn)算
- JavaScript 的版本是怎么回事?
- JavaScript和DOM的產(chǎn)生與發(fā)展
- DOM事件處理
- js的并行加載與順序執(zhí)行
- 正則表達(dá)式
- 當(dāng)遇上this時(shí)
- Javascript中apply、call、bind
- JavaScript的編譯過(guò)程與運(yùn)行機(jī)制
- 執(zhí)行上下文(Execution Context)
- javascript 作用域
- 分組中的函數(shù)表達(dá)式
- JS之constructor屬性
- Javascript 按位取反運(yùn)算符 (~)
- EvenLoop 事件循環(huán)
- 異步編程
- JavaScript的九個(gè)思維導(dǎo)圖
- JavaScript奇淫技巧
- JavaScript:shim和polyfill
- ===值得關(guān)注的庫(kù)===
- ==文章==
- JavaScript框架
- Angular 1.x
- 啟動(dòng)引導(dǎo)過(guò)程
- $scope作用域
- $q與promise
- ngRoute 和 ui-router
- 雙向數(shù)據(jù)綁定
- 規(guī)范和性能優(yōu)化
- 自定義指令
- Angular 事件
- lodash
- Test
