日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

[译]ng指令中的compile与link函数解析 转

發布時間:2024/1/8 编程问答 31 豆豆
生活随笔 收集整理的這篇文章主要介紹了 [译]ng指令中的compile与link函数解析 转 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

通常大家在使用ng中的指令的時候,用的鏈接函數最多的是link屬性,下面這篇文章將告訴大家complie,pre-link,post-link的用法與區別.

原文地址

angularjs里的指令非常神奇,允許你創建非常語義化以及高度重用的組件,可以理解為web components的先驅者.

網上已經有很多介紹怎么使用指令的文章以及相關書籍,相互比較的話,很少有介紹compile與link的區別,更別說pre-link與post-link了.

大部分教程只是簡單的說下compile會在ng內部用到,而且建議大家只用link屬性,大部分指令的例子里都是這樣的

這是非常不幸的,因為正確的理解這些函數的區別會提高你對ng內部運行機理的理解,有助于你開發更好的自定義指令.

所以跟著我一起來看下面的內容一步步的去了解這些函數是什么以及它們應該在什么時候用到

  • 本文假設你已經對指令有一定的了解了,如果沒有的話強烈建議你看看這篇文章AngularJS developer guide section on directives

NG中是怎么樣處理指令的

開始分析之前,先讓我們看看ng中是怎么樣處理指令的.

當瀏覽器渲染一個頁面時,本質上是讀html標識,然后建立dom節點,當dom樹創建完畢之后廣播一個事件給我們.

當你在頁面中使用script標簽加載ng應用程序代碼時,ng監聽上面的dom完成事件,查找帶有ng-app屬性的元素.

當找到這樣的元素之后,ng開始處理dom以這個元素的起點,所以假如ng-app被添加到html元素上,則ng就會從html元素開始處理dom.

從這個起點開始,ng開始遞歸查找所有子元素里面,符合應用程序里定義好的指令規則.

ng怎樣處理指令其實是依賴于它定義時的對象屬性的,你可以定義一個compile或者一個link函數,或者用pre-link和post-link函數來代替link.

所以這些函數的區別呢?為什么要使用它?以及什么時候使用它呢?

帶著這些問題跟著我一步一步來解答這些迷團吧

一段代碼

為了解釋這些函數的區別,下面我將使用一個簡單易懂的例子

  • 如果您有任何的問題,請不要猶豫趕緊在下面加上你的評論吧.

看看下面一段html標簽代碼

<level-one><level-two><level-three>Hello </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'));

結果非常簡單:讓ng來處理三個嵌套指令,并且每個指令都有自己的complile,pre-link,post-link函數,每個函數都會在控制臺里打印一行東西來標識自己.

這個例子能夠讓我們簡單的了解到ng在處理指令時,內部的流程

代碼輸出

下面是一個在控制臺輸出結果的截圖

如果想自己試一下這個例子的話,請點擊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

這個例子清晰的顯示出了ng在link之前編譯所有的指令,然后link要又分為了pre-link與post-link階段.

注意下,compile與pre-link的執行順序是依次執行的,但是post-link正好相反.

所以上面已經明確標識出了不同的階段,但是compile與pre-link有什么區別呢,都是相同的執行順序,為什么還要分成兩個不同的函數呢?

DOM

為了挖的更深一點,讓我們簡單的修改一下上面的代碼,它也會在各個函數里打印參數列表中的element變量

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里的輸出,除了輸出原始的html標記基本沒別的改變.

這個應該能夠加深我們對于這些函數上下文的理解.

再次運行代碼看看

輸出

下面是一個在控制臺輸出結果的截圖

假如你還想自己運行看看效果,可以點擊this plnkr,然后在控制臺里查看輸出結果.

觀察

輸出dom的結果可以暴露一些有趣的事情:dom內容在compile與pre-link兩個函數中是不一樣的

所以發生了什么呢?

Compile

我們已經知道當ng發現dom構建完成時就開始處理dom.

所以當ng在遍歷dom的時候,碰到level-one元素,從它的定義那里了解到,要執行一些必要的函數

因為compile函數定義在level-one指令的指令對象里,所以它會被調用并傳遞一個element對象作為它的參數

如果你仔細觀察,就會看到,瀏覽器創建這個element對象時,仍然是最原始的html標記

  • 在ng中,原始dom通常用來標識template element,所以我在定義compile函數參數時就用到了tElem名字,這個變量指向的就是template element.

一旦運行levelone指令中的compile函數,ng就會遞歸深度遍歷它的dom節點,然后在level-two與level-three上面重復這些操作.

Post-link

深入了解pre-link函數之前,讓我們來看看post-link函數.

  • 如果你在定義指令的時候只使用了一個link函數,那么ng會把這個函數當成post-link來處理,因此我們要先討論這個函數

當ng遍歷完所有的dom并運行完所有的compile函數之后,就反向調用相關聯的post-link函數.

dom現在開始反向,并執行post-link函數,因此,在之前這種反向的調用看起來有點奇怪,其實這樣做是非常有意義的.

當運行包含子指令的指令post-link時,反向的post-link規則可以保證它的子指令的post-link是已經運行過的.

所以,當運行level-one指令的post-link函數的時候,我們能夠保證level-two和level-three的post-link其實都已經運行過了.

這就是為什么人們都認為post-link是最安全或者默認的寫業務邏輯的地方.

但是為什么這里的element跟compile里的又不同呢?

一旦ng調用過指令的compile函數,就會創建一個template element的element實例對象,并且為它提供一個scope對象,這個scope有可能是新實例,也有可能是已經存在,可能是個子scope,也有可能是獨立的scope,這些都得依賴指令定義對象里的scope屬性值

所以當linking發生時,這個實例element以及scope對象已經是可用的了,并且被ng作為參數傳遞到post-link函數的參數列表中去.

  • 我個人總是使用iElem名稱來定義一個link函數的參數,并且它是指向element實例的

所以post-link(pre-link)函數的element參數對象是一個element實例而不是一個template element.

所以上面例子里的輸出是不同的

Pre-link

當寫了一個post-link函數,你可以保證在執行post-link函數的時候,它的所有子級指令的post-link函數是已經執行過的.

在大部分的情況下,它都可以做的更好,因此通常我們都會使用它來編寫指令代碼.

然而,ng為我們提供了一個附加的hook機制,那就是pre-link函數,它能夠保證在執行所有子指令的post-link函數之前.運行一些別的代碼.

這句話是值得反復推敲的

pre-link函數能夠保證在element實例上以及它的所有子指令的post-link運行之前執行.

所以它使的post-link函數反向執行是相當有意義的,它自己是原始的順序執行pre-link函數

這也意為著pre-link函數運行在它所有子指令的pre-link函數之前,所以完整的理由就是:

一個元素的pre-link函數能夠保證是運行在它所有的子指令的post-link與pre-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 函數

使用compile函數可以改變原始的dom(template element),在ng創建原始dom實例以及創建scope實例之前.

可以應用于當需要生成多個element實例,只有一個template element的情況,ng-repeat就是一個最好的例子,它就在是compile函數階段改變原始的dom生成多個原始dom節點,然后每個又生成element實例.因為compile只會運行一次,所以當你需要生成多個element實例的時候是可以提高性能的.

template element以及相關的屬性是做為參數傳遞給compile函數的,不過這時候scope是不能用的:

下面是函數樣子

/** * Compile function * * @param tElem - template element * @param tAttrs - attributes of the template element */ function(tElem, tAttrs){ // ... };

Pre-link 函數

使用pre-link函數可以運行一些業務代碼在ng執行完compile函數之后,但是在它所有子指令的post-link函數將要執行之前.

scope對象以及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){ // ... };

Post-link 函數

使用post-link函數來執行業務邏輯,在這個階段,它已經知道它所有的子指令已經編譯完成并且pre-link以及post-link函數已經執行完成.

這就是被認為是最安全以及默認的編寫業務邏輯代碼的原因.

scope實例以及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){ // ... };

總結

現在你應該對compile,pre-link,post-link這此函數之間的區別有了清晰的認識了吧.

如果還沒有的話,并且你是一個認真的ng開發者,那么我強烈建議你重新把這篇文章讀一讀直到你了解為止

理解這些概念非常重要,能夠幫助你理解ng原生指令的工作原理,也能幫你優化你自己的自定義指令.

如果還有問題的話,歡迎大家在下面評論里加上你的問題

以后還會接著分析關于指令里的其它兩個問題:

  • 指令使用transclusion屬性是怎么工作的?

  • 指令的controller函數是怎么關聯的

轉載于:https://www.cnblogs.com/GoodPingGe/p/4527493.html

總結

以上是生活随笔為你收集整理的[译]ng指令中的compile与link函数解析 转的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。