angular指令的compile,prelink 和 postlink

一. 指令模板选项有complie和link两个字段,两者之间存在如下关系:

  • 当compile字段存在时,link字段将被忽略,compile函数的返回值将作为link字段。
  • 当compile不存在,link字段存在时,angular通过这样directive.compile = valueFn(directive.link);包装一层,使用用户定义的link字段。
  • 当compile和link同事存在时,link被忽略,使用compile函数的返回值将作为link字段,
而link分为preLink和postLink两个阶段,从link字段或者compile函数的返回值来看:
  • 如果是函数,那么这样的link,会被认为是postLink。
  • 如果是对象,那么link.pre作为preLink函数,link.post作为postLink函数 (如下)
app.directive('myDirective', function () {
  return {
      compile: function () {
          return {
              pre: function () {
                  console.log('preLink');
              },
              post: function () {
                  console.log('postLink');
              }
          }
      }
  }
});

  

angular 启动后,会从ng-app所在元素,递归遍历所有子元素,查找出所有的指令(包括指令模板中的指令),对指令进行编译连接

编译会执行指令的compile(即使有多个指令实例,compile也只执行一次),·链接会执行指令的link(prelink,postlink)(有多少个指令实例,就执行几次)

二. compile,prelink,postlink的执行顺序

对于一个指令而言,首先会执行compile,然后执行prelink,最后执行postlink

对于所有的指令而言,执行顺序如下

compile的执行顺序

编译过程会按照从外到内(从父到子),从上到下(兄弟节点)以及priority的依次执行所有指令的compile,

prelink和postlink的执行顺序

所有指令的compile都执行完成后,会执行链接过程

链接过程会首先按照从外到内(从父到子)的顺序执行指令的prelink,然后再按照从内到外(从子到父)的顺序执行指令的postlink

从ng-app开始,会依次按照以下顺序执行prelink和postlink

1.如果有子节点指令,那么执行完本节点指令的prelink后,就会执行子节点指令的prelink,

2.如果没有子节点指令,那么会执行完本节点指令的prelink后,就会执行本节点指令的postlink,

3.如果有兄弟节点指令,那么执行完本节点指令的postlink后,就会去执行兄弟节点指令的prelink,

4.如果没有兄弟节点指令,那么执行完本节点指令的postlink后,就会去执行父节点指令的postlink,

以此类推,直到所有的指令的prelink和postlink执行完毕

 

测试链接:http://plnkr.co/edit/KtMs0H1pBsrOmXrFh9nf?p=preview

事例

html代码

<html ng-app="plunker">

  <head>
    <meta charset="utf-8" />
    <title>AngularJS Plunker</title>
    <script>document.write('<base href="' + document.location + '" />');</script>
    <link rel="stylesheet" href="style.css" />
    <script data-require="[email protected]" src="https://code.angularjs.org/1.2.22/angular.js" data-semver="1.2.22"></script>
    <script src="app.js"></script>
  </head>

  <body>
    <level-one>
      <level-two1>
        <level-three1>
          Hello {{name}}
        </level-three1>
        <level-four1>
          Hello {{name}}
        </level-four1>
      </level-two1>
      <level-two2>
        <level-three2>
          Hello {{name}}
        </level-three2>
        <level-four2>
          Hello {{name}}
        </level-four2>
      </level-two2>
    </level-one>
  </body>

</html>

  

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('levelTwo1', createDirective('levelTwo1'));
app.directive('levelThree1', createDirective('levelThree1'));
app.directive('levelFour1', createDirective('levelFour1'));
app.directive('levelTwo2', createDirective('levelTwo2'));
app.directive('levelThree2', createDirective('levelThree2'));
app.directive('levelFour2', createDirective('levelFour2'));

  结果

levelOne: compile => 
levelTwo1: compile => 
levelThree1: compile => 
levelFour1: compile => 
levelTwo2: compile => 
levelThree2: compile => 
levelFour2: compile => 
levelOne: pre link => 
levelTwo1: pre link => 
levelThree1: pre link => 
levelThree1: post link => 
levelFour1: pre link => 
levelFour1: post link => 
levelTwo1: post link => 
levelTwo2: pre link => 
levelThree2: pre link => 
levelThree2: post link => 
levelFour2: pre link => 
levelFour2: post link => 
levelTwo2: post link => 
levelOne: post link => 

  

可以看出:
  1. 所有的指令都是先compile,然后preLink,然后postLink。
  2. 节点指令的preLink是在所有子节点指令preLink,postLink之前所以一般这里就可以通过scope给子节点传递一定的信息
  3. 节点指令的postLink是在所有子节点指令preLink,postLink完毕之后,也就意味着,当父节点指令执行postLink时,子节点postLink已经都完成了,此时子dom树已经稳定,所以我们大部分dom操作,访问子节点都在这个阶段。
  4. 指令在link的过程,其实是一个深度优先遍历的过程,postLink的执行其实是一个回溯的过程。
  5. 节点上的可能有若干指令,在搜集的时候就会按一定顺序排列(通过byPriority排序),执行的时候,preLinks是正序执行,而postLinks则是倒序执行。

 三. compile,prelink,postlink的作用

Compile 函数

element以及相关的属性是做为参数传递给compile函数的,不过这时候scope是不能用的

使用compile函数可以改变原始的dom(template element),在ng创建原始dom实例以及创建scope实例之前.

可以应用于当需要生成多个element实例,只有一个template element的情况,ng-repeat就是一个最好的例子,它就在是compile函数阶段改变原始的dom生成多个原始dom节点,然后每个又生成element实例.因为compile只会运行一次,所以当你需要生成多个element实例的时候是可以提高性能的.

Pre-link 函数

preLink是在所有子节点指令preLink,postLink之前,所以一般这里就可以通过scope给子节点传递一定的信息.

scope对象以及element实例将会做为参数传递给pre-link函数:

Post-link 函数

postLink是在所有子节点指令preLink,postLink完毕之后,也就意味着,当父节点指令执行postLink时,子节点postLink已经都完成了,

此时子dom树已经稳定,所以我们大部分dom操作,访问子节点都在这个阶段。

可以再次函数做事件的绑定

这就是被认为是最安全以及默认的编写业务逻辑代码的原因.

scope实例以及element实例做为参数传递给post-link函数:

参考: https://blog.csdn.net/qq_28506819/article/details/72598457

猜你喜欢

转载自www.cnblogs.com/gaoBlog/p/9455133.html