摘要:實例元素及實例屬性都會作為參數(shù)傳遞到函式函式關(guān)連於此實例的實例元素實例元素的屬性結(jié)論到目前為止,但愿你有清楚的理解關(guān)于及之間的差異。
原文地址:https://987.tw/2014/09/03/ang...
AngularJS directives是令人驚艷的。它允許你創(chuàng)造高度語意且可重復(fù)利用的元件。在某種意義上你可以認(rèn)為它是極致的web components先驅(qū)者。
有許多很棒的文章,甚至是書籍,在教導(dǎo)你如何撰寫自己的directives。相較之下,只有少許的資訊談到有關(guān)compile及l(fā)ink函式的差異,更不用說有關(guān)pre-link及post-link函式差別。
大多數(shù)的導(dǎo)引只有簡單地提到compile函式主要由AngularJS在內(nèi)部使用,并且建議你只要用link函式,應(yīng)該能夠涵蓋大多數(shù)的使用案例。
這是十分不幸的,因為了解這些函式其中的差異能夠提升你的能力,更加的了解AngularJS內(nèi)部運(yùn)作,并且訂制出更好的directives。
所以跟著我,文章最后你將會正確地了解這些函式是什么以及什么時候該使用它們。
本文假設(shè)你已經(jīng)了解什么是AngularJS directive。如果不了解,我強(qiáng)烈建議你先閱讀AngularJS開發(fā)者指南的directive章節(jié)。
AngularJS如何處理directives?在我們開始之前,讓我打斷一下,先了解AngularJS是如何處理directives。
(1)當(dāng)瀏覽器渲染(render)頁面時,基本地讀取HTML標(biāo)簽,建立一個DOM,當(dāng)DOM準(zhǔn)備好時,廣播(broadcast)出一個事件(event)。
(2)當(dāng)你使用標(biāo)簽引入你的AngularJS程式碼到頁面時,AngularJS會監(jiān)聽(listen)該事件,一旦該事件發(fā)出,AngularJS便會開始走遍(traversing)DOM,尋找所有元素(element)中的屬性(attribute )是否具有ng-app。
(3)一旦找到具有該屬性的元素,AngularJS便以該元素作為起始點,進(jìn)行DOM 處理。如果在html元素的屬性內(nèi)設(shè)定ng-app,那么AngularJS將會從html元素開始處理 DOM。
(4)從起始點開始,AngularJS遞回地調(diào)查所有子元素,從你的AngularJS應(yīng)用程式中所定義的directives中去找尋相對應(yīng)的樣式。
AngularJS如何處理元素,取決于實際定義directive的物件(譯注:directive definition object)。你可以預(yù)先定義compile函式或link函式,兩者可同時存在?;蛘哌x擇性的定義pre-link及post-link這兩個函式來取代link函式,
所以,這些函式有什么差異?為什么及何時該使用這些函式?
堅持下去...
為了解釋這些差異,我會用程式碼來做示范,希望能夠更容易的理解。
考慮下列HTML標(biāo)簽:
Hello {{name}}
以及下列JavaScript:
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"));
目標(biāo)很簡單:讓AngularJS處理巢狀的三個directives,而每個directive都有自己的compile、pre-link及post-link函式,各函式輸出訊息至console,我們可以借此作為識別。
這讓我們可以一睹AngularJS是如何在背后處理這些directives。
輸出結(jié)果:
這是console輸出結(jié)果的截圖:
如果你要自己試試看,開啟這個plnkr鏈接,并在打開瀏覽器的Console。
開始分析第一件要注意的是,函式呼叫的順序:
// COMPILE階段 // levelOne: compile函式已呼叫 // levelTwo: compile函式已呼叫 // levelThree: compile函式已呼叫 // PRE-LINK階段 // levelOne: pre link函式已呼叫 // levelTwo: pre link函式已呼叫 // levelThree: pre link函式已呼叫 // POST-LINK階段 (注意到反向順序) // levelThree: post link函式已呼叫 // levelTwo: post link函式已呼叫 // levelOne: post link函式已呼叫
這個清除地展示AngularJS一開始compile所有directives,compile階段尚未連結(jié)scope,link階段也分成pre-link及post-link階段。
注意到呼叫compile及pre-link的順序是一致的,但是呼叫post-link的順序則是相反的。
所以在這里我們可以已經(jīng)清處的辨別這幾個不同的階段,但是compile與pre-link又有什么不同呢?它們也有同樣的順序,為什么要將它們分開?
文件物件模型(DOM)稍微深入一些,進(jìn)一步修改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()); } } } } } } app.directive("levelOne", createDirective("levelOne")); app.directive("levelTwo", createDirective("levelTwo")); app.directive("levelThree", createDirective("levelThree"));
注意到console.log額外的輸出訊息。沒有任何更動,仍然是最原始的標(biāo)簽。
這應(yīng)該能讓我們更詳細(xì)的了解函式的來龍去脈。
讓我們再次執(zhí)行程式碼。
輸出結(jié)果:
輸出DOM結(jié)果透漏某些有趣的東西:compile與pre-link階段的DOM不一樣。
所以,發(fā)生什么事?Compile
我們已經(jīng)學(xué)習(xí)到當(dāng)AngularJS偵測DOM準(zhǔn)備好時,會進(jìn)行DOM處理。
所以,當(dāng)AngularJS開始走遍DOM,它遇見
因為在levelOne的directive定義中,定義了compile函式,所以會呼叫此函式并帶入元素DOM作為函式的參數(shù)。
如果你靠近一點你會看到,在這個時機(jī)點,元素的DOM仍然是最初剛開始的DOM,系由瀏覽器根據(jù)原始HTML標(biāo)簽所創(chuàng)造出來的DOM。
在AngularJS里,經(jīng)常用樣板元素(template element)來提到原始的DOM,因此基于這個原因我個人用tElem來作為compile函式內(nèi)的參數(shù)名稱,用來表示樣板元素。
當(dāng)levelOne的compile執(zhí)行之后,AngularJS更深入且遞回地走入DOM,對
Post-link
在我們深入pre-link函式前,讓我們先看一下post-link函式。
如果你產(chǎn)生的directive只有l(wèi)ink函式,AngularJS會將它當(dāng)作是post-link函式。因為這個原因我們要在先討論它。
一旦AngularJS走到DOM的最后(底)并執(zhí)行完所有compile函式,它會往回(上)走并且執(zhí)行所有關(guān)聯(lián)的post-link函式。
現(xiàn)在DOM是用反方向在走遍,因此呼叫post-link函式是相反的順序。所以前幾分鐘看到相反順序覺得很奇怪,現(xiàn)在開始覺得合理了。
這相反順序保證所有的子元素post-link會先被執(zhí)行,接著才是父元素的post-link。
所以,當(dāng)
這就是為什么它被認(rèn)為是用來加入你的directive邏輯最安全以及預(yù)設(shè)的地方。
那元素的DOM呢?為什么在這里它們是不同的?
當(dāng)AngularJS呼叫了directive的compile函式之后,它會產(chǎn)生一個樣板元素(template element)的實例元素(instance element)(通常稱之為消滅實體),并且提供一個scope給這個實體。這個scope可以是全新的scope、繼承的子scope或孤立的scope,取決于相對應(yīng)directive定義物件內(nèi)scope屬性設(shè)定。
所以,到連結(jié)階段的時候,實例元素及scope已經(jīng)可以開始使用,并且AngularJS會將它作為函式參數(shù)傳遞到post-link函式。
Pre-link
當(dāng)撰寫post-link函式時,你可以保證所有子元素的post-link函式已經(jīng)執(zhí)行過。
在大多數(shù)的案例中,這個非常合理,因此它也是最常用來撰寫directive程式碼的地方。
然而,AngularJS提供了一個附加的鉤子,稱之為pre-link函式,程式碼會先被執(zhí)行,搶先在所有子元素的post-link被執(zhí)行之前。
再次強(qiáng)調(diào):
pre-link函式保證所有子元素的post-link被執(zhí)行前,先執(zhí)行pre-link函式,并且是在實體元素中執(zhí)行。
所以當(dāng)相反順序的呼叫post-link十分合理,那原始順序的呼叫pre-link也是十分合理。
回顧如果我們回顧之前原始輸出,我們可以清晰的辨認(rèn)出發(fā)生什么事:
// 這里的元素仍然是最原始的樣板標(biāo)簽 // COMPILE 階段 // levelOne: 原始DOM中呼叫compile函式 // levelTwo: 原始DOM中呼叫compile函式 // levelThree: 原始DOM中呼叫compile函式 // 從這里開始,元素已經(jīng)實例化且綁定了SCOPE // (例:NG-REPEAT 已有多重實例) // PRE-LINK 階段 // levelOne: 元素實例中呼叫pre link函式 // levelTwo: 元素實例中呼叫pre link函式 // levelThree: 元素實例中呼叫pre link函式 // POST-LINK 階段 (注意到順序相反) // levelThree: 元素實例中呼叫post link函式 // levelTwo: 元素實例中呼叫post link函式 // levelOne: 元素實例中呼叫post link函式摘要
回顧中我們可以描述不同的函式及使用案例如下:
Compile函式
在AngularJS產(chǎn)生實例及scope之前,使用compile函式來更動原始DOM(樣板元素)。
它可以有多個元素實例,但只會有一個樣板元素。ng-repeat就是這個案例的一個完美范例。它讓compile成為最佳的地方來進(jìn)行更動DOM,之后才會套用所有實例,因為只會執(zhí)行一次,所以當(dāng)你要消滅很多實例時,可以獲得很多效率上的提升。
樣板的元素及屬性都會作為參數(shù)傳遞到compile函式,但不會有scope傳入,因為還沒準(zhǔn)備好:
/** * Compile函式 * * @param tElem - 樣板元素 * @param tAttrs - 樣板元素的屬性 */ function(tElem, tAttrs){ // ... };
Pre-link函式
當(dāng)AngularJS已經(jīng)compile子元素,在任何子元素的post-link執(zhí)行之前,使用pre-link函式來實作邏輯。
Scope、實例元素及實例屬性都會作為參數(shù)傳遞到pre-link函式:
/** * Pre-link函式 * * @param scope - 關(guān)連於此實例的scope * @param iElem - 實例元素 * @param iAttrs - 實例元素的屬性 */ function(scope, iElem, iAttrs){ // ... };
Post-link函式
使用post-link來執(zhí)行邏輯,該邏輯知道所有子元素已經(jīng)編譯,并且所有子元素的pre-link及post-link都已經(jīng)被執(zhí)行。
基于這個理由,post-link認(rèn)為是最安全及預(yù)設(shè)的地方來撰寫你的程式碼。
Scope、實例元素及實例屬性都會作為參數(shù)傳遞到post-link函式:
/** * Post-link函式 *
@param scope - 關(guān)連於此實例的scope
@param iElem - 實例元素
@param iAttrs - 實例元素的屬性
*/
function(scope, iElem, iAttrs){
// ...
};
結(jié)論到目前為止,但愿你有清楚的理解關(guān)于compile、pre-link及post-link之間的差異。
如果沒有且你很認(rèn)真的在做AngularJS開發(fā),我強(qiáng)烈建議你再讀一次文章,直到你有穩(wěn)固的抓住其運(yùn)作原理。
了解這個重要的概念將會讓你更容易理解原生的AngularJS directive是如何運(yùn)作,并且如何最佳化你訂制的directives。
文章版權(quán)歸作者所有,未經(jīng)允許請勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請注明本文地址:http://m.hztianpu.com/yun/81471.html
摘要:方法三使用調(diào)用父作用域中的函數(shù)實例地址同樣采用了缺省寫法,運(yùn)行之后,彈出窗口布爾值或者字符,默認(rèn)值為這個配置選項可以讓我們提取包含在指令那個元素里面的內(nèi)容,再將它放置在指令模板的特定位置。 準(zhǔn)備代碼,會在實例中用到 var app = angular.module(app, []); angular指令定義大致如下 app.directive(directiveName, functi...
摘要:方法三使用調(diào)用父作用域中的函數(shù)實例地址同樣采用了缺省寫法,運(yùn)行之后,彈出窗口布爾值或者字符,默認(rèn)值為這個配置選項可以讓我們提取包含在指令那個元素里面的內(nèi)容,再將它放置在指令模板的特定位置。 準(zhǔn)備代碼,會在實例中用到 var app = angular.module(app, []); angular指令定義大致如下 app.directive(directiveName, functi...
摘要:方法三使用調(diào)用父作用域中的函數(shù)實例地址同樣采用了缺省寫法,運(yùn)行之后,彈出窗口布爾值或者字符,默認(rèn)值為這個配置選項可以讓我們提取包含在指令那個元素里面的內(nèi)容,再將它放置在指令模板的特定位置。 準(zhǔn)備代碼,會在實例中用到 var app = angular.module(app, []); angular指令定義大致如下 app.directive(directiveName, functi...
摘要:在運(yùn)用的時候,運(yùn)用自定義指令可以寫一些組件,非常方便。這里給大家分享一些關(guān)于自定義指令的知識。 在運(yùn)用angularjs的時候,運(yùn)用自定義指令可以寫一些組件,非常方便。這里給大家分享一些關(guān)于angular自定義指令的知識。 1. 定義 對于指令,可以把它簡單的理解成在特定DOM元素上運(yùn)行的函數(shù),指令可以擴(kuò)展這個元素 的功能。 2.定義指令的方法: angular.module(myAp...
閱讀 3319·2021-11-24 09:39
閱讀 3260·2021-10-21 09:38
閱讀 2454·2019-08-29 15:28
閱讀 3797·2019-08-26 12:23
閱讀 2671·2019-08-26 12:19
閱讀 1410·2019-08-23 12:44
閱讀 2178·2019-08-23 12:02
閱讀 1120·2019-08-22 17:05