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