1、編譯階段
第一個(gè)階段是編譯階段。在編譯階段,AngularJS會(huì)遍歷整個(gè)HTML文檔并根據(jù)JavaScript中的指令定義來處理頁面上聲明的指令。
一旦對指令和其中的子模板進(jìn)行遍歷或編譯,編譯后的模板會(huì)返回一個(gè)叫做模板函數(shù)的函數(shù)。我們有機(jī)會(huì)在指令的模板函數(shù)被返回前,對編譯后的DOM樹進(jìn)行修改。
在這個(gè)時(shí)間點(diǎn)DOM樹還沒有進(jìn)行數(shù)據(jù)綁定,意味著如果此時(shí)對DOM樹進(jìn)行操作只會(huì)有很少的性能開銷?;诖它c(diǎn), ng-repeat和ng-transclude等內(nèi)置指令會(huì)在這個(gè)時(shí)候,也就是還未與任何作用域數(shù)據(jù)進(jìn)行綁定時(shí)對DOM進(jìn)行操作。
2、compile(對象或函數(shù))
compile選項(xiàng)可以返回一個(gè)對象或函數(shù)。
compile選項(xiàng)本身并不會(huì)被頻繁使用,但是link函數(shù)則會(huì)被經(jīng)常使用。本質(zhì)上,當(dāng)我們設(shè)置了link選項(xiàng),實(shí)際上是創(chuàng)建了一個(gè)postLink()鏈接函數(shù),以便compile()函數(shù)可以定義鏈接函數(shù)。
通常情況下,如果設(shè)置了compile函數(shù),說明我們希望在指令和實(shí)時(shí)數(shù)據(jù)被放到DOM中之前進(jìn)行DOM操作,在這個(gè)函數(shù)中進(jìn)行諸如添加和刪除節(jié)點(diǎn)等DOM操作是安全的。
注:compile和link選項(xiàng)是互斥的。如果同時(shí)設(shè)置了這兩個(gè)選項(xiàng),那么會(huì)把compile所返回的函數(shù)當(dāng)作鏈接函數(shù),而link選項(xiàng)本身則會(huì)被忽略。
不要進(jìn)行DOM事件監(jiān)聽器的注冊:這個(gè)操作應(yīng)該在鏈接函數(shù)中完成。
compile: function(tEle, tAttrs, transcludeFn) {
var tplEl = angular.element('
var h2 = tplEl.find('h2');
h2.attr('type', tAttrs.type);
h2.attr('ng-model', tAttrs.ngModel);
h2.val("hello");
tEle.replaceWith(tplEl);
return function(scope, ele, attrs) {
// 連接函數(shù)
};
}
編譯函數(shù)負(fù)責(zé)對模板DOM進(jìn)行轉(zhuǎn)換。
鏈接函數(shù)負(fù)責(zé)將作用域和DOM進(jìn)行鏈接。
3、鏈接
用link函數(shù)創(chuàng)建可以操作DOM的指令。
鏈接函數(shù)是可選的。如果定義了編譯函數(shù),它會(huì)返回鏈接函數(shù),因此當(dāng)兩個(gè)函數(shù)都定義了時(shí),編譯函數(shù)會(huì)重載鏈接函數(shù)。如果我們的指令很簡單,并且不需要額外的設(shè)置,可以從工廠函數(shù) (回
調(diào)函數(shù))返回一個(gè)函數(shù)來代替對象。如果這樣做了,這個(gè)函數(shù)就是鏈接函數(shù)。
下面兩種定義指令的方式在功能上是完全一樣的:
angular.module('myApp', [])
.directive('myDirective', function() {
return {
pre: function(tElement, tAttrs, transclude) {
// 在子元素被鏈接之前執(zhí)行
// 在這里進(jìn)行Don轉(zhuǎn)換不安全
// 之后調(diào)用'lihk'h函數(shù)將無法定位要鏈接的元素
},
post: function(scope, iElement, iAttrs, controller) {
// 在子元素被鏈接之后執(zhí)行
// 如果在這里省略掉編譯選項(xiàng)
//在這里執(zhí)行DOM轉(zhuǎn)換和鏈接函數(shù)一樣安全嗎
}
};
});
angular.module('myApp', [])
.directive('myDirective', function() {
return {
link: function(scope, ele, attrs) {
return {
pre: function(tElement, tAttrs, transclude) {
// 在子元素被鏈接之前執(zhí)行
// 在這里進(jìn)行Don轉(zhuǎn)換不安全
// 之后調(diào)用'lihk'h函數(shù)將無法定位要鏈接的元素
},
post: function(scope, iElement, iAttrs, controller) {
// 在子元素被鏈接之后執(zhí)行
// 如果在這里省略掉編譯選項(xiàng)
//在這里執(zhí)行DOM轉(zhuǎn)換和鏈接函數(shù)一樣安全嗎
}
}
}
});
下面看一下鏈接函數(shù)中的參數(shù):
scope
指令用來在其內(nèi)部注冊監(jiān)聽器的作用域。
iElement
iElement參數(shù)代表實(shí)例元素,指使用此指令的元素。在postLink函數(shù)中我們應(yīng)該只操作此元素的子元素,因?yàn)樽釉匾呀?jīng)被鏈接過了。
iAttrs
iAttrs參數(shù)代表實(shí)例屬性,是一個(gè)由定義在元素上的屬性組成的標(biāo)準(zhǔn)化列表,可以在所有指令的鏈接函數(shù)間共享。會(huì)以javascript對象的形式進(jìn)行傳遞。
controller
controller參數(shù)指向require選項(xiàng)定義的控制器。如果沒有設(shè)置 require選項(xiàng) ,那么controller參數(shù)的值為undefined。
控制器在所有的指令間共享,因此指令可以將控制器當(dāng)作通信通道(公共API)。如果設(shè)置了多個(gè)require,那么這個(gè)參數(shù)會(huì)是一個(gè)由控制器實(shí)例組成的數(shù)組,而不只是一個(gè)單獨(dú)的控制器。