上篇文章《【AngularJS】——核心特性之指令》簡單的介紹了一下指令的類型和簡單應用。那麼,指令具體是怎麼運行的呢?讓我們來一起揭開指令的神祕面紗。
我們知道,指令的本質其實是一個替換的過程。指令的核心步驟就是compile(編譯)和link(連接)。
1、Link函數
指令生成出的模板其實沒有太多意義,除非它在特定的scope下編譯。默認情況下,指令並不會創建新的子scope。更多的,它使用父scope。也就是說,如果指令存在於一個controller下,它就會使用這個controller的scope。 如何運用scope,我們要用到一個叫做 link 的函數。它由指令定義對象中的link屬性配置。讓我們來改變一下我們的 helloWorld 指令,當用戶在一個輸入框中輸入一種顏色的名稱時,Hello World 文字的背景色自動發生變化。同時,當用戶在 Hello World 文字上點擊時,背景色變回白色。 相應的HTML標記和指令如下:
<html ng-app="myapp">
<head>
<title>Angular JS Custom Directives</title>
<!-- 引入ng庫 -->
<script src="http://ajax.googleapis.com/ajax/libs/angularjs/1.2.15/angular.min.js"></script>
<script>
var app = angular.module('myapp', []);
app.directive('helloWorld', function() {
return {
restrict: 'AE',
replace: true,
template: '<p style="background-color:{{color}}">Hello World',
/*link函數*/
link: function(scope, elem, attrs) {
elem.bind('click', function() {
elem.css('background-color','green');
scope.$apply(function() {
scope.color = "green";
});
});
elem.bind('mouseover', function() {
elem.css('cursor', 'pointer');
});
}
};
});
</script>
</head>
<body>
<input type="text" ng-model="color" placeholder="Enter a color" />
<hello-World/>
</body>
</html>
我們注意到指令定義中的 link 函數。 它有三個參數:
scope – 指令的scope。在我們的例子中,指令的scope就是父controller的scope。
elem – 指令的jQLite(jQuery的子集)包裝DOM元素。如果你在引入AngularJS之前引入了jQuery,那麼這個元素就是jQuery元素,而不是jQLite元素。由於這個元素已經被jQuery/jQLite包裝了,所以我們就在進行DOM操作的時候就不需要再使用 $()來進行包裝。
attr – 一個包含了指令所在元素的屬性的標準化的參數對象。舉個例子,你給一個HTML元素添加了一些屬性:,那麼可以在 link 函數中通過 attrs.someAttribute 來使用它。
link函數主要用來爲DOM元素添加事件監聽、監視模型屬性變化、以及更新DOM。在上面的指令代碼片段中,我們添加了兩個事件, click,和 mouseover。click 處理函數用來重置 <p> 的背景色,而 mouseover 處理函數改變鼠標爲 pointer。在模板中有一個表達式 {{color}},當父scope中的 color 發生變化時,它用來改變 Hello World 文字的背景色。
2、compile函數
compile 函數在 link 函數被執行之前用來做一些DOM改造。它接收下面的參數:
tElement – 指令所在的元素
attrs – 元素上賦予的參數的標準化列表
要注意的是 compile 函數不能訪問 scope,並且必須返回一個 link 函數。但是如果沒有設置 compile 函數,你可以正常地配置 link 函數,(有了compile,就不能用link,link函數由compile返回)。compile函數可以寫成如下的形式:
app.directive('test', function() {
return {
compile: function(tElem,attrs) {
//do optional DOM transformation here
return function(scope,elem,attrs) {
//linking function here
};
}
};
});
3、指令執行過程
整個指令的執行過程就分爲:加載階段-->編譯階段-->鏈接階段。
當應用引導啓動的時候,Angular開始使用 $compile 服務遍歷DOM元素。這個服務基於註冊過的指令在標記文本中搜索指令。一旦所有的指令都被識別後,Angular執行他們的 compile 方法。如前面所講的,compile 方法返回一個 link 函數,被添加到稍後執行的 link 函數列表中。這被稱爲編譯階段。如果一個指令需要被克隆很多次(比如 ng-repeat),compile函數只在編譯階段被執行一次,複製這些模板,但是link 函數會針對每個被複制的實例被執行。所以分開處理,讓我們在性能上有一定的提高。這也說明了爲什麼在 compile 函數中不能訪問到scope對象。 在編譯階段之後,就開始了鏈接(linking)階段。在這個階段,所有收集的 link 函數將被一一執行。指令創造出來的模板會在正確的scope下被解析和處理,然後返回具有事件響應的真實的DOM節點。