AngularJS太棒了。它容許你建立高度語義化和可重用的組建。從某種意義上說,您能夠將它們視爲Web組件的最終先驅。html
關於如何寫自定義的指令已經出如今不少好的文章和書中了。相反,很騷有涉及到compile
和link
函數的不一樣,更不要說pre-link
與post-link
函數了。git
大多數教程都簡潔的說起到compile
函數主要被用在AngularJS內部而且建議你只使用link
函數這能夠覆蓋大多數自定義指令使用場景。angularjs
所以,跟誰我,在本文的最後,您將確切知道這些功能是什麼以及什麼時候應該使用它們。github
This article assumes that you already know what an AngularJS directive is. If not, I would highly recommend reading the AngularJS developer guide section on directives first.
在咱們開始以前,讓咱們先來分解AngularJS如何處理指令。瀏覽器
當瀏覽器渲染一個頁面時,它必定會去閱讀HTML標籤,建立DOM而且在DOM準備就緒時廣播事件。安全
當你使用<script></script>
在你的代碼頁中引入AngulaJS時,AngularJS監聽那事件當它一旦收到事件,它開始遍歷DOM,查詢一個有ng-app
屬性的標籤。微信
當這個標籤被查找到,AngularJS開始使用該特定元素做爲起點處理DOM。所以,若是在html
元素上設置ng-app
屬性,AngularJS將開始處理從html
元素開始的DOM。app
從這時起,AngularJS遞歸地查詢全部的子元素,尋找對應於你的AngularJS應用程序中定義的指令的模式。ide
AngularJS如何處理元素取決於實際的指令定義對象。你定義了一個compile
函數,一個link
函數或者二者。或者你能夠定義一個pre-link
函數和一個post-link
函數來代替link
函數。函數
那麼,全部這些功能之間有什麼區別,以及爲何或何時應用它們?
跟緊我...
爲了解釋差別,我將使用一些但願易於理解的示例代碼。
考慮下面的HTML標記:
<level-one> <level-two> <level-three> Hello {{name}} </level-three> </level-two> </level-one>
和一下JS:
var app = angular.module('plunker', []); function createDirective(name){ return function(){ return { restrict: 'E', compile: function(tElem, tAttrs){ console.log(name + ': compile'); return { pre: function(scope, iElem, iAttrs){ console.log(name + ': pre link'); }, post: function(scope, iElem, iAttrs){ console.log(name + ': post link'); } } } } } } app.directive('levelOne', createDirective('levelOne')); app.directive('levelTwo', createDirective('levelTwo')); app.directive('levelThree', createDirective('levelThree'));
目的很簡單:使AngularJS處理三個嵌套的指令,每一個指令都有本身的compile
,pre-link
,post-link
函數去log一行文本保證咱們可以辨識它們。
這應該使咱們可以首先了解AngularJS處理指令後發生的狀況。
如下是控制檯中輸出的屏幕截圖:
本身嘗試一下,僅僅打開this plnkr 看一眼控制檯輸出
首先注意到的是函數的調用順序
// COMPILE PHASE // levelOne: compile function is called // levelTwo: compile function is called // levelThree: compile function is called // PRE-LINK PHASE // levelOne: pre link function is called // levelTwo: pre link function is called // levelThree: pre link function is called // POST-LINK PHASE (Notice the reverse order) // levelThree: post link function is called // levelTwo: post link function is called // levelOne: post link function is called
這清晰的證實了AngularJS是如何第一編譯全部指令在把他們link他們是scope以前,而且在link階段被分解爲了pre-link
,post-link
階段
注意到compile
與pre-link
函數調用順序是相同的可是post-link
與他們恰好相反。
因此在這一點上咱們已經能夠清楚地識別出不一樣的階段,那麼compile
與pre-link
有什麼區別了?他們執行的順序是相同的,那爲何要把他們分開?
爲了深刻研究,讓咱們更新JavaScript,以便在每一個函數調用期間輸出元素的DOM:
var app = angular.module('plunker', []); function createDirective(name){ return function(){ return { restrict: 'E', compile: function(tElem, tAttrs){ console.log(name + ': compile => ' + tElem.html()); return { pre: function(scope, iElem, iAttrs){ console.log(name + ': pre link => ' + iElem.html()); }, post: function(scope, iElem, iAttrs){ console.log(name + ': post link => ' + iElem.html()); } } } } } }
注意console.log
行中的額外輸出。沒有其餘更改,原始標記仍然使用。
如下是新添加的代碼的輸出截圖:
再次,若是你想本身嘗試一下,只需打開this plnkr並看看控制檯。
打印出的DOM顯示一些有趣的事:DOM在compile
與pre-link
階段是不一樣的。
咱們已經知道AngularJS在檢測到DOM已經準備就緒時會處理DOM。
因此當AngularJS開始遍歷DOM,它碰到<level-one>
元素而且瞭解到它在匹配咱們定義的指令它有額外的動做須要執行
因爲在evelOne
指令定義對象中定義了一個compile
函數,他被調用而且相對應的元素DOM被看成參數傳入
若是你仔細查看,在這點上,元素的DOM仍然是最初由瀏覽器使用原始HTML標記建立的DOM。
在AngularJS中 original DOM 常被指代爲template element,所以我我的鐘意使用
tElem
做爲參數名,以表明template element。
一旦levelOne
指令的compile
函數已經運行,AngularJS遞歸地便利深層的DOM並對<level-two>
和<level-three>
元素執行一樣的編譯步驟。
在深刻pre-link
函數以前,讓咱們先看一看post-link
函數
若是你建立的指令中只有link 函數,AngularJS將它看成
post-link
函數,這也是咱們先討論它的緣由。
在AngularJS向下傳遞DOM並運行全部編譯compile
以後,它再次反向遍歷並運行全部相關的post-link
函數。
DOM如今在相反的方向上遍歷,所以post-link
函數按相反的順序調用。因此,雖然相反的順序幾分鐘前看起來很奇怪,但它如今開始變得很是有意義。
這個相反的順序保證了在父元素的post-link
函數運行時全部子元素的post-link
函數已經運行。
因此當<level-one>
的post-link
執行時,咱們保證<level-two>
<level-three>
的post-link
函數已經執行。
這就是爲何它被認爲是最安全和默認的添加指令邏輯的緣由。
可是元素的DOM呢?這裏爲何不一樣?
一單AngularJS已經調用了compile
函數,它建立了一個對應模板元素的instance element併爲instance提供一個scope。The scope能夠爲一個新的scope或者存在的一個,子scope或者隔離的scope,取決於相應指令定義對象的scope
屬性。
所以,在link發生時,實例元素和範圍已經可用,而且它們被AngularJS做爲參數傳遞給post-link
函數。
所以控制檯輸出有差別
在編寫post-link
函數時,能夠保證全部子元素的post-link
函數都已經執行。
在大多數狀況下,這是很是有意義的,所以它是編寫指令代碼最經常使用的地方。
然而AngularJS提供了一個附加的鉤子,the pre-link
函數,你能夠在全部子元素執行post-link
函數以前執行你的代碼。
值得重申的是:pre-link
函數能保證在全部子istance element執行post-link
前先執行。
所以,儘管post-link
函數以相反的順序調用很是合理,但如今再次以原始順序調用全部pre-link
函數是很是有意義的。
元素的pre-link
函數被保障在任意子元素的pre-link
與post-link
先執行
若是咱們如今回顧原始輸出,咱們能夠清楚地認識到發生了什麼:
// HERE THE ELEMENTS ARE STILL THE ORIGINAL TEMPLATE ELEMENTS // COMPILE PHASE // levelOne: compile function is called on original DOM // levelTwo: compile function is called on original DOM // levelThree: compile function is called on original DOM // AS OF HERE, THE ELEMENTS HAVE BEEN INSTANTIATED AND // ARE BOUND TO A SCOPE // (E.G. NG-REPEAT WOULD HAVE MULTIPLE INSTANCES) // PRE-LINK PHASE // levelOne: pre link function is called on element instance // levelTwo: pre link function is called on element instance // levelThree: pre link function is called on element instance // POST-LINK PHASE (Notice the reverse order) // levelThree: post link function is called on element instance // levelTwo: post link function is called on element instance // levelOne: post link function is called on element instance
回想起來,咱們能夠以下描述不一樣的功能和用例:
使用compile
來實如今AngularJS建立它的實例以前和建立範圍以前更改原始DOM(模板元素)。
雖然能夠有多個元素實例,但只有一個模板元素。ng-repeat
指令是這種狀況的一個很好的例子。這使得編譯功能成爲修改DOM的理想場所,之後應該將其應用於全部實例,由於它只會運行一次,所以若是要刪除大量實例,則會極大地提升性能。
模板元素和屬性做爲參數傳遞給編譯函數,但沒有scope可用:
/** * Compile function * * @param tElem - template element * @param tAttrs - attributes of the template element */ function(tElem, tAttrs){ // ... };
使用pre-link
函數來實現當AngularJS已經編譯子元素時,但在子元素的任何post-link
函數被調用以前運行的邏輯。
scope,instance element和屬性做爲參數傳遞給pre-link
函數:
/** * Pre-link function * * @param scope - scope associated with this istance * @param iElem - instance element * @param iAttrs - attributes of the instance element */ function(scope, iElem, iAttrs){ // ... };
Here you can see example code of official AngularJS directives that use a pre-link function.
使用post-link
函數執行邏輯,全部的子元素已經被編譯而且全部子元素的pre-link
post-link
已經執行。
因此post-link
是被看成最安全和默認的位置放置你的代碼。
scope,instance element和屬性做爲參數傳遞給post-link
函數:
/** * Post-link function * * @param scope - scope associated with this istance * @param iElem - instance element * @param iAttrs - attributes of the instance element */ function(scope, iElem, iAttrs){ // ... };
若有任何問題和建議歡迎發送至郵箱討論:<Tommy.White.h.li@gmail.com>
編寫不易,若您以爲對您有幫助,歡迎打賞
微信:
支付寶: