通常大家在使用ng中的指令的时候,用的链接函数最多的是link属性,下面这篇文章将告诉大家complie,pre-link,post-link的用法与区别.

原文地址

angularjs里的指令非常神奇,允许你创建非常语义化以及高度重用的组件,可以理解为web components的先驱者.

网上已经有很多介绍怎么使用指令的文章以及相关书籍,相互比较的话,很少有介绍compilelink的区别,更别说pre-linkpost-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-linkpost-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 

这个例子清晰的显示出了nglink之前编译所有的指令,然后link要又分为了pre-linkpost-link阶段.

注意下,compilepre-link的执行顺序是依次执行的,但是post-link正好相反.

所以上面已经明确标识出了不同的阶段,但是compilepre-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内容在compilepre-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-twolevel-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-twolevel-threepost-link其实都已经运行过了.

这就是为什么人们都认为post-link是最安全或者默认的写业务逻辑的地方.

但是为什么这里的elementcompile里的又不同呢?

一旦ng调用过指令的compile函数,就会创建一个template elementelement实例对象,并且为它提供一个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-linkpre-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函数解析 转相关推荐

  1. 必须正确理解的---ng指令中的compile与link函数解析

    这个绝对是深入的知识,但看完之后,对NG的理解就很利害啦. http://www.ifeenan.com/angularjs/2014-09-04-%5B%E8%AF%91%5DNG%E6%8C%87 ...

  2. 【转】angularjs指令中的compile与link函数详解

    这篇文章主要介绍了angularjs指令中的compile与link函数详解,本文同时诉大家complie,pre-link,post-link的用法与区别等内容,需要的朋友可以参考下 通常大家在使用 ...

  3. angularjs指令中的compile与link函数详解

    通常大家在使用ng中的指令的时候,用的链接函数最多的是link属性,下面这篇文章将告诉大家complie,pre-link,post-link的用法与区别. angularjs里的指令非常神奇,允许你 ...

  4. 走进AngularJs(二) ng模板中常用指令的使用方式

    通过使用模板,我们可以把model和controller中的数据组装起来呈现给浏览器,还可以通过数据绑定,实时更新视图,让我们的页面变成动态的.ng的模板真是让我爱不释手.学习ng道路还很漫长,从模板 ...

  5. html input ng model,Angular自定义指令中传递ngModel

    根据项目需要,希望写一个自定义指令,将指令中传递的数据(比如:my-model="name"),绑定到template中的ng-model中,并且可以被外部作用域使用. 参考了&l ...

  6. Angular 中的依赖注入link

    Angular 中的依赖注入link 依赖注入(DI)是一种重要的应用设计模式. Angular 有自己的 DI 框架,在设计应用时常会用到它,以提升它们的开发效率和模块化程度. 依赖,是当类需要执行 ...

  7. angularJS在创建指令需要注意的问题(指令中使用ngRepeat)

    现在发现,当初的自己真的是太菜了,为什么你在指令中更改数据,没有作用呢?这其实是原型链的问题. 详细的我就不在这里说了,有位大神早已发布了这个内容,在这里复制个地址给大家,有兴趣的可以看看 http: ...

  8. angularjs compile和link

    原文:http://www.cnblogs.com/GoodPingGe/p/4361354.html ************************************************ ...

  9. 编译和链接(compile and link)

    编译和链接(compile and link) 编译: 1    由预处理程序执行C 源文件中的预处理指令 2        C编译器把经过预处理的C代码文件编译成汇编代码文件 3 汇编编译器把汇编代 ...

最新文章

  1. 【数据安全案例】交警计算机系统再遭***,交通违法记录随意删除
  2. 数据结构 链表(一)
  3. nashorn 性能_Nashorn简介
  4. 上传源文件至虚拟服务器,C# 通过WebService上传视频文件到服务器虚拟机下源码...
  5. 单线程多线程_面试系列 redis为什么快amp;单线程amp;多线程
  6. docker安装chirpstack_用docker-compose快速部署ChirpStack
  7. SQL语法集锦一:SQL语句实现表的横向聚合
  8. PHPStorm更换主题,调整背景和字体颜色
  9. 如何把qq挂到云服务器,云服务器挂QQ软件常用方法和注意问题
  10. VSCode 之 设置 settings.json 配置文件
  11. meltdown linux检测,检查你的Linux PC是否受Meltdown和Spectre安全缺陷影响
  12. 几种常用的显示器分辨率
  13. 怎样在Win7桌面上显示“我的电脑”
  14. 【Vue】Vue数据及事件绑定
  15. Ajax详解第六篇(jQuery实现Ajax)
  16. P4 程序设计语法学习
  17. 泸州职业技术学院计算机单招试题,2021年泸州职业技术学院单招语文考试模拟试题库...
  18. IntelliJ IDEA中的神仙插件 写代码必备!
  19. cdm 图片 转_从CDP到CDM的转变
  20. 小米笔记本装linux教程视频教程,小米笔记本安装Ubuntu 18.04系统后使用特殊键的方法...

热门文章

  1. 京东搭配购怎么设置,操作指南来啦
  2. 深度隐藏文件或文件夹原理与应用
  3. SpringCloud禁用Eureka自我保护模式
  4. bios计算机专业术语全称,bios是什么意思 电脑进入bios方法【图文】
  5. VFP获取微信小程序用户openID,易如反掌
  6. SSM Chapter 12 SpringMVC扩展和SSM框架整合
  7. 为什么word文档或EXCET表格从电脑传到手机上格式就变了
  8. Python极简讲义一本书入门机器学习和数据分析--自学笔记
  9. 1.DDD中的领域对象、值对象、聚合根
  10. Docker容器之网络模式