AngularJS is awesome, it allows you to create highly semantic and reusable components. In a sense, you can think of them as the ultimate precursors to web components.
There are many good articles and books on how to write custom instructions. On the contrary, it is very interesting that it involves the difference between the compile
and link
functions, not to mention the pre-link
and post-link
functions. .
Most tutorials succinctly mention that the compile
function is mainly used inside AngularJS and recommend that you only use the link
function which is fine Covers most custom instruction usage scenarios.
So, follow me, and by the end of this article, you will know exactly what these features are and when you should use them.
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.
Before we get started, let’s break down how AngularJS handles directives.
When the browser renders a page, it must read the HTML tags, create the DOM and broadcast events when the DOM is ready.
When you use <script></script>
to introduce AngulaJS into your code page, AngularJS listens for that event. Once it receives the event, it starts traversing the DOM, Query a tag with ng-app
attribute.
When this tag is found, AngularJS starts processing the DOM using that specific element as a starting point. So if you set the ng-app
attribute on the html
element, AngularJS will start processing the DOM starting from the html
element.
From this point on, AngularJS recursively queries all child elements, looking for patterns that correspond to directives defined in your AngularJS application.
How AngularJS handles elements depends on the actual directive definition object. You define a compile
function, a link
function, or both. Or you can define a pre-link
function and a post-link
function instead of the link
function.
So, what are the differences between all these features, and why or when to apply them?
Follow Me...
To explain the differences, I'm going to use some sample code that I hope will be easy to understand.
Consider the following HTML markup:
<level-one> <level-two> <level-three> Hello {{name}} </level-three> </level-two> </level-one>
and the 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'));
The purpose is simple: make AngularJS handle three nested directives, each with its own The compile
, pre-link
, post-link
function logs a line of text to ensure that we can identify them.
This should allow us to first understand what happens after AngularJS processes the directive.
Here is a screenshot of the output in the console:
Try it yourself, just open this plnkr and take a look at the console output
The first thing to notice is the calling sequence of the functions
// 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
This clearly proves how AngularJS compiles all instructions first and links them to the scope. before, and is broken down into pre-link
, post-link
stage
in the link stage. Note that compile
and pre- The link
function calling order is the same but post-link
is just the opposite.
So at this point we can clearly identify the different stages, so what is the difference between compile
and pre-link
? They execute in the same order, so why separate them?
To dig deeper, let’s update the JavaScript to output the element’s DOM during each function call:
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()); } } } } } }
NOTEconsole.log# Extra output in ## lines. No other changes, the original markup is still used.
compile and
pre-link stages.
element and understands that it is matching the directive we defined and it has additional actions to perform
compile function is defined in the
evelOne directive definition object, he is called and the corresponding element DOM is passed in as a parameter
在AngularJS中 original DOM 常被指代为template element,因此我个人钟意使用tElem
作为参数名,以代表template element。
一旦levelOne
指令的compile
函数已经运行,AngularJS递归地便利深层的DOM并对<level-two></level-two>
和<level-three></level-three>
元素执行同样的编译步骤。
在深入pre-link
函数之前,让我们先看一看post-link
函数
如果你创建的指令中只有link 函数,AngularJS将它当作post-link
函数,这也是我们先讨论它的原因。
在AngularJS向下传递DOM并运行所有编译compile
之后,它再次反向遍历并运行所有相关的post-link
函数。
DOM现在在相反的方向上遍历,因此post-link
函数按相反的顺序调用。所以,虽然相反的顺序几分钟前看起来很奇怪,但它现在开始变得非常有意义。
这个相反的顺序保证了在父元素的post-link
函数运行时所有子元素的post-link
函数已经运行。
所以当<level-one></level-one>
的post-link
执行时,我们保证<level-two></level-two>
<level-three></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){ // ... };
The above is the detailed content of The truth about AngularJS directives compile and link functions. For more information, please follow other related articles on the PHP Chinese website!