ホームページ >ウェブフロントエンド >jsチュートリアル >AngularJS ディレクティブのコンパイルおよびリンク関数に関する真実

AngularJS ディレクティブのコンパイルおよびリンク関数に関する真実

寻∝梦
寻∝梦オリジナル
2018-09-07 14:40:081265ブラウズ

AngularJS は素晴らしい、高度にセマンティックで再利用可能なコンポーネントを作成できます。ある意味、これらは Web コンポーネントの究極の先駆者であると考えることができます。

カスタム命令の書き方については、優れた記事や本がたくさんあります。それどころか、pre-link と compile 関数と link 関数の違いが関係していることは非常に興味深いです。 code>ポストリンク code>関数。 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是如何处理指令的

在我们开始之前,让我们先来分解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处理指令后发生的情况。

输出

以下是控制台中输出的屏幕截图:
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阶段

注意到compilepre-link函数调用顺序是相同的但是post-link与他们刚好相反。

所以在这一点上我们已经可以清楚地识别出不同的阶段,那么compilepre-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 ディレクティブのコンパイルおよびリンク関数に関する真実

再次,如果你想自己尝试一下,只需打开this plnkr并看看控制台。

观察

打印出的DOM显示一些有趣的事:DOM在compilepre-link阶段是不同的。

这里发生了什么

编译

我们已经知道AngularJS在检测到DOM已经准备就绪时会处理DOM。

所以当AngularJS开始遍历DOM,它碰到<level-one></level-one>元素并且了解到它在匹配我们定义的指令它有额外的动作需要执行

由于在evelOne指令定义对象中定义了一个compile

ほとんどのチュートリアルでは、compile 関数が主に AngularJS は内部的に使用されるため、ほとんどのカスタム ディレクティブの使用シナリオに対応できる link 関数のみを使用することをお勧めします。

それでは、この記事を読み終えるまでに、これらの機能が何なのか、いつ使用する必要があるのか​​が正確にわかるようになりますので、フォローしてください。 🎜
この記事は、AngularJS ディレクティブが何であるかをすでに理解していることを前提としています。まだ理解していない場合は、最初に AngularJS 開発者ガイドのディレクティブに関するセクションを読むことを強くお勧めします。

AngularJS がディレクティブを処理する方法

🎜始める前に、AngularJS がディレクティブを処理する方法を詳しく見てみましょう。 🎜🎜ブラウザがページをレンダリングするときは、HTML タグを読み取り、DOM を作成し、DOM の準備ができたらイベントをブロードキャストする必要があります。 🎜🎜 <script></script> を使用して AngulaJS をコード ページに導入すると、AngularJS はイベントを受信すると、DOM の走査とクエリの実行を開始します。 ng-app 属性のタグ。 🎜🎜このタグが見つかると、AngularJS はその特定の要素を開始点として使用して DOM の処理を​​開始します。したがって、html 要素に ng-app 属性を設定すると、AngularJS は html 要素から DOM の処理を​​開始します。 🎜🎜この時点から、AngularJS はすべての子要素を再帰的にクエリし、AngularJS アプリケーションで定義されたディレクティブに対応するパターンを探します。 🎜🎜AngularJS が要素を処理する方法は、実際のディレクティブ定義オブジェクトによって異なります。 compile 関数、link 関数、またはその両方を定義します。または、link 関数の代わりに pre-link 関数と post-link 関数を定義することもできます。 🎜🎜それでは、これらすべての機能の違いは何でしょうか?また、それらを適用する理由やタイミングを教えてください。 🎜🎜フォローしてください... 🎜

コードのデモ

🎜 違いを説明するために、理解しやすいと思われるサンプル コードを使用します。 🎜🎜次の HTML マークアップを考えてみましょう: 🎜
// 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
🎜 と次の JS: 🎜
/**
* Compile function
* 
* @param tElem - template element
* @param tAttrs - attributes of the template element
*/
function(tElem, tAttrs){

    // ...

};
🎜 目的は簡単です: AngularJS に 3 つのネストされた命令を処理させ、それぞれに独自の compile,pre- linkpost-link 関数は、テキスト行をログに記録して、それらを確実に認識できるようにします。 🎜🎜これにより、AngularJS がディレクティブを処理した後に何が起こるかをまず理解できるようになります。 🎜

出力

🎜以下は、コンソールの出力のスクリーンショットです。
AngularJS ディレクティブのコンパイルおよびリンク関数に関する真実
この plnkr を開いて、自分で試してみてください。コンソール出力を見てください 🎜

分析の開始

🎜 最初に注目すべきことは、関数呼び出しの順序です🎜
/**
* 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){

    // ...

};
🎜これは、AngularJS がスコープにリンクする前に最初にすべての命令をコンパイルし、リンクステージ pre-link フェーズと post-link フェーズに分解されます🎜🎜compilepre-link の順序に注意してください。 code> 関数呼び出しは同じですが、post-link はその逆です。 🎜🎜この時点で、さまざまな段階を明確に識別できます。では、compilepre-link の違いは何でしょうか?これらは同じ順序で実行されるのに、なぜ分離するのでしょうか? 🎜

DOM

🎜 さらに詳しく調べるために、各関数呼び出し中に要素の DOM を出力するように JavaScript を更新しましょう: 🎜
/**
* 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){

    // ...

};
🎜 console.log 行出力の余分な部分に注目してください。他に変更はなく、元のマークアップが引き続き使用されます。 🎜

出力

🎜 以下は、新しく追加されたコードの出力のスクリーンショットです。
AngularJS ディレクティブのコンパイルおよびリンク関数に関する真実🎜🎜もう一度、自分で試してみたい場合は、この plnkr を開いてコンソールを見てください。 🎜

観察

🎜 印刷された DOM には興味深いことが示されています。DOM は compile 段階と pre-link 段階で異なります。 🎜

ここで何が起こっているのか

コンパイル

🎜 AngularJS が DOM の準備ができたことを検出するとそれを処理することはすでにわかっています。 🎜🎜したがって、AngularJS が DOM のトラバースを開始すると、<level-one></level-one> 要素に遭遇し、それが定義したディレクティブと一致しており、実行する追加のアクションがあることを理解します。 > compile 関数は evelOne ディレクティブ定義オブジェクトで定義されており、この関数が呼び出され、対応する要素 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 ディレクティブのコンパイルおよびリンク関数に関する真実

这个相反的顺序保证了在父元素的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 ディレクティブのコンパイルおよびリンク関数に関する真実

回顾

如果我们现在回顾原始输出,我们可以清楚地认识到发生了什么:

// 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 ディレクティブのコンパイルおよびリンク関数に関する真実の詳細内容です。詳細については、PHP 中国語 Web サイトの他の関連記事を参照してください。

声明:
この記事の内容はネチズンが自主的に寄稿したものであり、著作権は原著者に帰属します。このサイトは、それに相当する法的責任を負いません。盗作または侵害の疑いのあるコンテンツを見つけた場合は、admin@php.cn までご連絡ください。