首頁  >  文章  >  web前端  >  AngularJS指令compile和link函數的真相

AngularJS指令compile和link函數的真相

寻∝梦
寻∝梦原創
2018-09-07 14:40:081162瀏覽

AngularJS太棒了,它允許你創建高度語義化和可重用的組成。從某種意義上說,您可以將它們視為Web元件的最終先驅。

關於如何寫自訂的指令已經出現在很多好的文章和書中了。相反,很騷有涉及到compilelink函數的不同,更不要說pre-linkpost-link函數了。

大多數教程都簡潔的提及到compile函數主要被用在AngularJS內部並且建議你只使用link函數這可以覆蓋大多數自訂指令使用場景。

因此,跟誰我,在本文的最後,您將確切知道這些功能是什麼以及何時應該使用它們。

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。 從這時起,AngularJS遞歸地查詢所有的子元素,尋找對應於你的AngularJS應用程式中定義的指令的模式。

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 看一眼控制台輸出AngularJS指令compile和link函數的真相開始分析

首先註意到的是函數的呼叫順序

// 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有什麼區別了?他們執行的順序是相同的,那為什麼要把他們分開? The DOM

為了深入研究,讓我們更新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

行中的額外輸出。沒有其他更改,原始標記仍然使用。

輸出

以下是新加入的程式碼的輸出截圖:


AngularJS指令compile和link函數的真相#再一次,如果你想自己嘗試一下,只要打開this plnkr並看看控制台。

觀察

列印出的DOM顯示一些有趣的事:DOM在

compile

pre-link階段是不同的。 這裡發生了什麼

編譯

我們已經知道AngularJS在偵測到DOM已經準備好時會處理DOM。

所以當AngularJS開始遍歷DOM,它碰到

<level-one></level-one>

元素並且了解到它在匹配我們定義的指令它有額外的動作需要執行

由於在evelOne

指令定義物件中定義了一個

compile函數,他被呼叫並且相對應的元素DOM被當作參數傳入如果你仔細查看,在這點上,元素的DOM仍然是最初由瀏覽器使用原始HTML標記創建的DOM。

在AngularJS中 original DOM 常被指代为template element,因此我个人钟意使用tElem作为参数名,以代表template element。

一旦levelOne指令的compile函数已经运行,AngularJS递归地便利深层的DOM并对<level-two></level-two><level-three></level-three>元素执行同样的编译步骤。

Post-link

在深入pre-link函数之前,让我们先看一看post-link函数

如果你创建的指令中只有link 函数,AngularJS将它当作post-link函数,这也是我们先讨论它的原因。

在AngularJS向下传递DOM并运行所有编译compile之后,它再次反向遍历并运行所有相关的post-link函数。

DOM现在在相反的方向上遍历,因此post-link函数按相反的顺序调用。所以,虽然相反的顺序几分钟前看起来很奇怪,但它现在开始变得非常有意义。
AngularJS指令compile和link函數的真相

这个相反的顺序保证了在父元素的post-link函数运行时所有子元素的post-link函数已经运行。

所以当<level-one></level-one>post-link执行时,我们保证<level-two></level-two> <level-three></level-three>post-link函数已经执行。

这就是为什么它被认为是最安全和默认的添加指令逻辑的原因。

但是元素的DOM呢?这里为什么不同?

一单AngularJS已经调用了compile函数,它创建了一个对应模板元素的instance element并为instance提供一个scope。The scope可以为一个新的scope或者存在的一个,子scope或者隔离的scope,取决于相应指令定义对象的scope属性。

因此,在link发生时,实例元素和范围已经可用,并且它们被AngularJS作为参数传递给post-link函数。

因此控制台输出有差异

Pre-link

在编写post-link函数时,可以保证所有子元素的post-link函数都已经执行。

在大多数情况下,这是非常有意义的,因此它是编写指令代码最常用的地方。

然而AngularJS提供了一个附加的钩子,the pre-link函数,你可以在所有子元素执行post-link函数之前执行你的代码。

值得重申的是:
pre-link函数能保证在所有子istance element执行post-link前先执行。

因此,尽管post-link函数以相反的顺序调用非常合理,但现在再次以原始顺序调用所有pre-link函数是非常有意义的。

元素的pre-link函数被保障在任意子元素的pre-linkpost-link先执行
AngularJS指令compile和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 function

使用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 function

使用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 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){

    // ...

};

最后,本篇文章到这就结束 (想看更多就到PHP中文网angularjs学习手册中学习),有问题的可以在下方留言提问

以上是AngularJS指令compile和link函數的真相的詳細內容。更多資訊請關注PHP中文網其他相關文章!

陳述:
本文內容由網友自願投稿,版權歸原作者所有。本站不承擔相應的法律責任。如發現涉嫌抄襲或侵權的內容,請聯絡admin@php.cn