视频1 视频21 视频41 视频61 视频文章1 视频文章21 视频文章41 视频文章61 推荐1 推荐3 推荐5 推荐7 推荐9 推荐11 推荐13 推荐15 推荐17 推荐19 推荐21 推荐23 推荐25 推荐27 推荐29 推荐31 推荐33 推荐35 推荐37 推荐39 推荐41 推荐43 推荐45 推荐47 推荐49 关键词1 关键词101 关键词201 关键词301 关键词401 关键词501 关键词601 关键词701 关键词801 关键词901 关键词1001 关键词1101 关键词1201 关键词1301 关键词1401 关键词1501 关键词1601 关键词1701 关键词1801 关键词1901 视频扩展1 视频扩展6 视频扩展11 视频扩展16 文章1 文章201 文章401 文章601 文章801 文章1001 资讯1 资讯501 资讯1001 资讯1501 标签1 标签501 标签1001 关键词1 关键词501 关键词1001 关键词1501 专题2001
AngularJS指令compile和link函数的
2020-11-27 19:31:34 责编:小采
文档

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处理指令后发生的情况。

输出

以下是控制台中输出的屏幕截图:

自己尝试一下,仅仅打开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行中的额外输出。没有其他更改,原始标记仍然使用。

输出

以下是新添加的代码的输出截图:

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

观察

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

这里发生了什么

编译

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

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

由于在evelOne指令定义对象中定义了一个compile函数,他被调用并且相对应的元素DOM被当作参数传入

如果你仔细查看,在这点上,元素的DOM仍然是最初由浏览器使用原始HTML标记创建的DOM。

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

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

Post-link

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

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

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

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

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

所以当<level-one>post-link执行时,我们保证<level-two> <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先执行

回顾

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

// 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学习手册中学习),有问题的可以在下方留言提问

下载本文
显示全文
专题