1、編譯階段
第一個階段是編譯階段。在編譯階段,AngularJS會遍歷整個HTML文檔并根據JavaScript中的指令定義來處理頁面上聲明的指令。
一旦對指令和其中的子模板進行遍歷或編譯,編譯后的模板會返回一個叫做模板函數的函數。我們有機會在指令的模板函數被返回前,對編譯后的DOM樹進行修改。
在這個時間點DOM樹還沒有進行數據綁定,意味著如果此時對DOM樹進行操作只會有很少的性能開銷。基于此點, ng-repeat和ng-transclude等內置指令會在這個時候,也就是還未與任何作用域數據進行綁定時對DOM進行操作。
?
2、compile(對象或函數)
compile選項可以返回一個對象或函數。
compile選項本身并不會被頻繁使用,但是link函數則會被經常使用。本質上,當我們設置了link選項,實際上是創建了一個postLink()鏈接函數,以便compile()函數可以定義鏈接函數。
通常情況下,如果設置了compile函數,說明我們希望在指令和實時數據被放到DOM中之前進行DOM操作,在這個函數中進行諸如添加和刪除節點等DOM操作是安全的。
注:compile和link選項是互斥的。如果同時設置了這兩個選項,那么會把compile所返回的函數當作鏈接函數,而link選項本身則會被忽略。
不要進行DOM事件監聽器的注冊:這個操作應該在鏈接函數中完成。
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) {
// 連接函數
};
}
編譯函數負責對模板DOM進行轉換。
鏈接函數負責將作用域和DOM進行鏈接。
3、鏈接
用link函數創建可以操作DOM的指令。
鏈接函數是可選的。如果定義了編譯函數,它會返回鏈接函數,因此當兩個函數都定義了時,編譯函數會重載鏈接函數。如果我們的指令很簡單,并且不需要額外的設置,可以從工廠函數 (回
調函數)返回一個函數來代替對象。如果這樣做了,這個函數就是鏈接函數。
下面兩種定義指令的方式在功能上是完全一樣的:
angular.module('myApp', [])
.directive('myDirective', function() {
return {
pre: function(tElement, tAttrs, transclude) {
// 在子元素被鏈接之前執行
// 在這里進行Don轉換不安全
// 之后調用'lihk'h函數將無法定位要鏈接的元素
},
post: function(scope, iElement, iAttrs, controller) {
// 在子元素被鏈接之后執行
// 如果在這里省略掉編譯選項
//在這里執行DOM轉換和鏈接函數一樣安全嗎
}
};
});
angular.module('myApp', [])
.directive('myDirective', function() {
return {
link: function(scope, ele, attrs) {
return {
pre: function(tElement, tAttrs, transclude) {
// 在子元素被鏈接之前執行
// 在這里進行Don轉換不安全
// 之后調用'lihk'h函數將無法定位要鏈接的元素
},
post: function(scope, iElement, iAttrs, controller) {
// 在子元素被鏈接之后執行
// 如果在這里省略掉編譯選項
//在這里執行DOM轉換和鏈接函數一樣安全嗎
}
}
}
});
如果你編寫自定義的compile函數,則自定義的link函數無效應為compile會返回一個link函數
下面看一下鏈接函數中的參數:
scope
指令用來在其內部注冊監聽器的作用域。
iElement
iElement參數代表實例元素,指使用此指令的元素。在postLink函數中我們應該只操作此元素的子元素,因為子元素已經被鏈接過了。
iAttrs
iAttrs參數代表實例屬性,是一個由定義在元素上的屬性組成的標準化列表,可以在所有指令的鏈接函數間共享。會以javascript對象的形式進行傳遞。
controller
controller 參 數 指 向 require 選 項 定 義 的 控 制 器 。 如 果 沒 有 設 置 require 選 項 , 那 么controller參數的值為undefined。
控制器在所有的指令間共享,因此指令可以將控制器當作通信通道(公共API)。如果設置了多個require,那么這個參數會是一個由控制器實例組成的數組,而不只是一個單獨的控制器。
總結:
(1)compile函數的作用就是對指令的模板函數進行轉換。
(2)link函數是在模型和視圖之間建立關聯,包括在元素上注冊監聽事件。
(3)scope在連接階段才被綁定到元素上,所以在compile階段操作scope回報錯誤。
(4)一般情況下,我們只寫link函數就夠了。
(5)請注意,如果你編寫自定義的compile函數,則自定義的link函數無效應為compile會返回一個link函數