angular指令的compile,prelink 和 postlink以及controller
一. 指令模板选项有complie和link两个字段,两者之间存在如下关系:
- 当compile字段存在时,link字段将被忽略,compile函数的返回值将作为link字段。
- 当compile不存在,link字段存在时,
angular
通过这样directive.compile = valueFn(directive.link);
包装一层,使用用户定义的link字段。 - 当compile和link同事存在时,link被忽略,使用compile函数的返回值将作为link字段,
- 如果是函数,那么这样的link,会被认为是postLink。
- 如果是对象,那么link.pre作为preLink函数,link.post作为postLink函数 (如下)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 | 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代码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 | var app = angular.module( 'plunker' , []); function createDirective(name){ return function(){ return { restrict: 'E' , controller: function () { console.log(name + ': controller => ' ); }, 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' )); |
结果
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 | app.js:11 levelOne: compile => app.js:11 levelTwo1: compile => app.js:11 levelThree1: compile => app.js:11 levelFour1: compile => app.js:11 levelTwo2: compile => app.js:11 levelThree2: compile => app.js:11 levelFour2: compile => app.js:8 levelOne: controller => app.js:14 levelOne: pre link => app.js:8 levelTwo1: controller => app.js:14 levelTwo1: pre link => app.js:8 levelThree1: controller => app.js:14 levelThree1: pre link => app.js:17 levelThree1: post link => app.js:8 levelFour1: controller => app.js:14 levelFour1: pre link => app.js:17 levelFour1: post link => app.js:17 levelTwo1: post link => app.js:8 levelTwo2: controller => app.js:14 levelTwo2: pre link => app.js:8 levelThree2: controller => app.js:14 levelThree2: pre link => app.js:17 levelThree2: post link => app.js:8 levelFour2: controller => app.js:14 levelFour2: pre link => app.js:17 levelFour2: post link => app.js:17 levelTwo2: post link => app.js:17 levelOne: post link => |
- 所有的指令都是先compile,然后preLink,然后postLink。
- 节点指令的preLink是在所有子节点指令preLink,postLink之前,所以一般这里就可以通过scope给子节点传递一定的信息。
- 节点指令的postLink是在所有子节点指令preLink,postLink完毕之后,也就意味着,当父节点指令执行postLink时,子节点postLink已经都完成了,此时
子dom树已经稳定
,所以我们大部分dom操作,访问子节点都在这个阶段。 - 指令在link的过程,其实是一个深度优先遍历的过程,postLink的执行其实是一个回溯的过程。
- 节点上的可能有若干指令,在搜集的时候就会按一定顺序排列(通过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函数:
controller
之前已经讲过compile,prelink 和 postlink的关系,执行顺序,以及作用,这里在追加controller
controler是在compile之后,prelink之前执行,可以在controller中定义一些数据供prelink或postlink使用
或者定义一些方法供其他的指令调用。
可以说controller提供一些外部接口,供其他指令调用,link只处理当前指令。
指令的controller可以通过 指令名+Ctrl 的方式注入到其他指令(指令的第四个参数)
参考: https://blog.csdn.net/qq_28506819/article/details/72598457
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 基于Microsoft.Extensions.AI核心库实现RAG应用
· Linux系列:如何用heaptrack跟踪.NET程序的非托管内存泄露
· 开发者必知的日志记录最佳实践
· SQL Server 2025 AI相关能力初探
· Linux系列:如何用 C#调用 C方法造成内存泄露
· 震惊!C++程序真的从main开始吗?99%的程序员都答错了
· 别再用vector<bool>了!Google高级工程师:这可能是STL最大的设计失误
· 单元测试从入门到精通
· 【硬核科普】Trae如何「偷看」你的代码?零基础破解AI编程运行原理
· 上周热点回顾(3.3-3.9)