AngularJS太棒了。它容许你建立高度语义化和可重用的组建。从某种意义上说,您能够将它们视为Web组件的最终先驱。html
关于如何写自定义的指令已经出如今不少好的文章和书中了。相反,很骚有涉及到compile
和link
函数的不一样,更不要说pre-link
与post-link
函数了。git
大多数教程都简洁的说起到compile
函数主要被用在AngularJS内部而且建议你只使用link
函数这能够覆盖大多数自定义指令使用场景。angularjs
所以,跟谁我,在本文的最后,您将确切知道这些功能是什么以及什么时候应该使用它们。github
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如何处理指令。浏览器
当浏览器渲染一个页面时,它必定会去阅读HTML标签,建立DOM而且在DOM准备就绪时广播事件。安全
当你使用<script></script>
在你的代码页中引入AngulaJS时,AngularJS监听那事件当它一旦收到事件,它开始遍历DOM,查询一个有ng-app
属性的标签。微信
当这个标签被查找到,AngularJS开始使用该特定元素做为起点处理DOM。所以,若是在html
元素上设置ng-app
属性,AngularJS将开始处理从html
元素开始的DOM。app
从这时起,AngularJS递归地查询全部的子元素,寻找对应于你的AngularJS应用程序中定义的指令的模式。ide
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
阶段
注意到compile
与pre-link
函数调用顺序是相同的可是post-link
与他们恰好相反。
因此在这一点上咱们已经能够清楚地识别出不一样的阶段,那么compile
与pre-link
有什么区别了?他们执行的顺序是相同的,那为何要把他们分开?
为了深刻研究,让咱们更新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在compile
与pre-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>
元素执行一样的编译步骤。
在深刻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
函数。
所以控制台输出有差别
在编写post-link
函数时,能够保证全部子元素的post-link
函数都已经执行。
在大多数状况下,这是很是有意义的,所以它是编写指令代码最经常使用的地方。
然而AngularJS提供了一个附加的钩子,the pre-link
函数,你能够在全部子元素执行post-link
函数以前执行你的代码。
值得重申的是:pre-link
函数能保证在全部子istance element执行post-link
前先执行。
所以,尽管post-link
函数以相反的顺序调用很是合理,但如今再次以原始顺序调用全部pre-link
函数是很是有意义的。
元素的pre-link
函数被保障在任意子元素的pre-link
与post-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
来实如今AngularJS建立它的实例以前和建立范围以前更改原始DOM(模板元素)。
虽然能够有多个元素实例,但只有一个模板元素。ng-repeat
指令是这种状况的一个很好的例子。这使得编译功能成为修改DOM的理想场所,之后应该将其应用于全部实例,由于它只会运行一次,所以若是要删除大量实例,则会极大地提升性能。
模板元素和属性做为参数传递给编译函数,但没有scope可用:
/** * Compile function * * @param tElem - template element * @param tAttrs - attributes of the template element */ function(tElem, tAttrs){ // ... };
使用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
函数执行逻辑,全部的子元素已经被编译而且全部子元素的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){ // ... };
若有任何问题和建议欢迎发送至邮箱讨论:<Tommy.White.h.li@gmail.com>
编写不易,若您以为对您有帮助,欢迎打赏
微信:
支付宝: