Understanding Priorities in AngularJS Directive Definition Objects

理解AngularJS指令定义对象的优先级

初学者提升对Angular理解的一个重要步骤就是去学习和理解Angular在处理模板是所经历的不同阶段,包括compile阶段、pre-link阶段和post-link阶段。

出于以上原因,这是一个经常被探讨的话题。我最喜欢的是Jurgen Van de Moere在他 博客
里写的一篇文章,观点比较简洁清楚。然而,关于指令(directive)定义对象的可选优先级的说明比较少,或者说一般都不是很清晰明确。我在本文里希望用Moere那篇文章相似的方式来解释Angular优先级的工作原理。

处理嵌套指令

这里没有必要完全重复Moere的文章,你可以在他的博客上阅读。但是我还是想要先展示他的文章的总体轮廓,因为这也是我在搞清楚优先级时使用的。

下面是从他的博客里直接拿的HTML例子:

     
         
               
      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'));

然后他解释道:“我们的目的很简单,让AngularJs处理三个嵌套的指令,每个指令有自己的 compile

pre-link
以及
post-link
函数,函数执行后会打印一条log日志帮助我们识别它们。”

实际上,我们是想通过观察这些函数调用的顺序,从而识别出 compile, pre-link 和 post-link 阶段的执行顺序。

最后,如果你运行上面的例子,你可以在控制台看到下面的输出:

levelOne: compile
levelTwo: compile
levelThree: compile
levelOne: pre link
levelTwo: pre link
levelThree: pre link
levelThree: post link
levelTwo: post link
levelOne: post link

Angular在 compile 和 pre-link 阶段时从上到下遍历DOM,然后在post-link阶段以相反的顺序遍历。这确保在父元素的post-link完成之前,先将给定父元素的所有子元素完全确定下来,这样可以确保添加任何可能影响子元素的逻辑都是安全的。

关于这些不同的阶段还有很多要说的,但理解Angular处理指令这部分是决定在不同阶段采取什么操作的关键。

处理单个DOM元素上的多个指令

现在,如果不是嵌套的指令呢?我们在同一个元素上有多个指令。 compile, pre-link, 以及 post-link 函数触发的顺序是怎样的呢?这就是引入优先级概念的原因了。

Angular文档
里这样说道:

当多个指令被定义在同一个DOM元素上时,有时候指定指令执行的顺序是很有必要的。 优先级(priority)
就是用于在指令的compile函数被调用之前将它们进行排序。pre-link函数也是按照优先级确定的顺序来运行的,但是post-link函数按照相反的顺序执行。相同优先级的指令的执行顺序没有定义,默认优先级的值为 0

鉴于刚才给出的嵌套指令的演示,你现在应该对期望的结果有一个很好的预测。但是如果你也像我一样,直到看到示例并自己动手实践后才会真正的对结论清晰明了,那么继续让我们修改上面的示例,以验证这点。

我们要做的改动很微小。我们将在一个单独的元素上声明三个指令而不是使用嵌套指令。

现在我们既然将指令作为属性来使用,那需要确保设置下面的属性:

restrict: 'A’

现在让我们看一下控制台输出的结果:

levelOne: compile
levelThree: compile
levelTwo: compile
levelOne: pre link
levelThree: pre link
levelTwo: pre link
levelTwo: post link
levelThree: post link
levelOne: post link

这个过程中发生了什么呢?结果就是,当同一个元素上声明多个具有相同优先级的directive时(这个例子中它们优先级都是0,因为0是默认值),它们将按照字母顺序进行处理而不是声明的顺序。大家可以试试看。修改directive的名字以及修改它们声明的顺序。只要它们优先级相同,那么就会按照字母顺序执行。

在大多数情况下,按照字母顺序来处理指令并没有什么问题。但是如果同一个元素上的某些指令是依赖其他指令的,那就会产生问题了。这种情况下,我们可以指定指令的优先级来控制它们被处理的顺序。

让我们修改一下示例。使用的还是之前的HTML,但是现在我们指定了它们的优先级:

var app = angular.module('myApp', []);

    function createDirective(name, priority) {    
      return function () {    
        return {      
          priority: priority,
          restrict: 'A',      
          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', 1));  
  app.directive('levelTwo', createDirective('levelTwo', 2));  
  app.directive('levelThree', createDirective('levelThree', 3));

看一下控制台我们可以发现,它们按照优先级从高到低的顺序执行指令的compile和pre-link阶段,post-link执行的顺序相反。

levelThree: compile
levelTwo: compile
levelOne: compile
levelThree: pre link
levelTwo: pre link
levelOne: pre link
levelOne: post link
levelTwo: post link
levelThree: post link

即使我已经知道,嵌套的指令会按照相反的顺序进行post-link,但我在第一次看到它的时候还是有点措手不及,所以我在这里清楚地说明了一下。如果我们切换了指令的优先级,那么这些指令在嵌套时会得到相对应的输出。

例如:

app.directive('levelOne', createDirective('levelOne', 3));  
app.directive('levelTwo', createDirective('levelTwo', 2));  
app.directive('levelThree', createDirective('levelThree', 1));

输出:

levelOne: compile
levelTwo: compile
levelThree: compile
levelOne: pre link
levelTwo: pre link
levelThree: pre link
levelThree: post link
levelTwo: post link
levelOne: post link

最后,还有一点要注意。大多数Angular的核心指令(core directives)优先级为0,这意味着如果希望在核心指令或者其他优先级为0的指令之后运行自定义指令,你需要将这个自定义优先级设置为负值。例如:

app.directive('levelOne', createDirective('levelOne', 0));  
app.directive('levelTwo', createDirective('levelTwo', 1));  
app.directive('levelThree', createDirective('levelThree', -1));

下面是输出结果:

levelTwo: compile
levelOne: compile
levelThree: compile
levelTwp: pre link
levelOne: pre link
levelThree: pre link
levelThree: post link
levelOne: post link
levelTwo: post link

以上示例基本上涵盖了优先级的概念。这看起来相当简单,但跟大多数概念一样,看起来简单,但在第一次遇到时可能有点疑惑,最好用一些例子来说明一下,来帮助你预测你将得到的输出结果。

众成翻译稿源:众成翻译 (源链) | 关于 | 阅读提示

本站遵循[CC BY-NC-SA 4.0]。如您有版权、意见投诉等问题,请通过eMail联系我们处理。
酷辣虫 » 前端开发 » Understanding Priorities in AngularJS Directive Definition Objects

喜欢 (0)or分享给?

专业 x 专注 x 聚合 x 分享 CC BY-NC-SA 4.0

使用声明 | 英豪名录