angular源码分析5-编译连接

 

在文档加载完(全部资源加载完)之后,angular调用angularInit函数初始化。找到含有ng-app的元素,调用bootstrap启动。建立$injector服务,加载ng-app绑定的模块及其子模块(调用模块的config,run,处理service,factory等服务缓存在providerCache)。而后从含ng-app的元素编译连接指令。html

编译连接

编译连接架构

1.html文件ready后,初始化angular。node

2.建立$injector,建立ng模块,初始化config, run, 注册的服务。经过第1464行开始编译连接。angularjs

 3.编译web

经过compileNodes递归编译当前节点和子节点,得到编译后的连接函数。返回闭包连接函数publicLinkFn,用于连接过程。bootstrap

使用compileNodes函数编译节点,把当前节点连接函数和直系子节点的compositeLinkFn存储到linkFns数组中,返回当前节点的闭包函数compositeLinkFn,执行访问linkFns。递归编译节点后,连接函数的树状结构数据也已生成。数组

编译节点上的指令,返回连接函数nodeLinkFn,用于连接当前节点和递归连接子节点。缓存

连接的入口,调用publicLinkFn开始连接函数。在compositeLinkFn中调用nodeLinkFn递归连接节点和子节点。闭包

publicLinkFn函数实际上调用的是compositeLinkFn函数,而compositeLinkFn函数是compileNodes函数的返回值,实际上它返回了一个闭包。compositeLinkFn函数操做的是linkFns,linkFns包含两部分:当前节点的连接函数和子节点的连接函数childLinkFn,而childLinkFn自己也是一个compositeLinkFn(在子节点上递归调用compileNodes的返回结果),因此实际的连接过程就是递归调用nodeLinkFn函数。架构

编译

编译入口

在上面的代码中使用了$compile服务,使用$compile服务用于编译连接,是在publishExternalAPI时挂载在ng模块下的服务。第1490行,调用compile服务开始全局编译连接。compile(element)全局编译,element为含有ng-app的angular做用范围的入口元素,调用完该函数返回连接函数pubLinkFn。调用pubLinkFn,scope为$rootScope,全局指令连接。app

 

编译过程

compile函数

 

先判断编译的起始节点是不是jqLite包装的对象,若是不是,把元素包装成jqLite对象。若是编译的节点是文本节点,使用<span>对文本节点包装。 而后使用compileNodes函数开始全局编译当前节点及其全部子节点,返回当前节点及其子节点的组合的连接函数。最后返回一个闭包函数,用于指令的连接,该函数可以访问到连接函数compositeLinkFn。

compileNodes函数

使用深度优先搜索编译当前节点及其子节点。处理每一个节点的过程:第一步使用collectDirectives函数收集节点上的指令,第二步使用函数applyDirectivesToNode编译节点的指令,而且返回节点的指令的连接函数。第三步递归调用compileNodes函数编译节点的子节点。第四步,把返回的指令的连接函数和子节点的连接函数放到数组linkFns中。当同一级的节点全部节点如上述过程处理完后,返回函数compositeLinkFn。该函数为闭包函数,能访问到由当前级的全部节点指令的有关连接函数组合成linkFns数组结构。

遍历过程实例,ng-app绑定在body元素上,div1,div2,div3,div4,...,div7为子节点。以下所示:

 

 

遍历过程为深度优先搜索的过程。

1 从body节点开始遍历

2 遍历到div1,收集编译div1上指令,获取div1指令上的编译函数,获得div1Link

3 遍历到div3,收集编译div3上指令,获取div3指令上的编译函数,获得div3Link

linkFns:[(0,div3Link)]

4 回溯到div1,遍历div4,收集编译div4上指令,获取div4指令上的编译函数,获得div4Link

5 遍历div7,收集编译div7上指令,获取div7指令上的编译函数,获得div7Link

linkFns:[(0,div7Link)]

6 回溯到div1,遍历div5,收集div5上指令,获取div5指令上的编译函数,获得div5Link

linkFns:[(0,div5Link)]

7 回溯到div1,div1没有未被访问的子节点,

linkFns:[(0,div3Link),(1,div4Link,div4ChildLink),(2,div5Link)]

8 回溯到body,如遍历左分支,遍历右分支

9 ...

10 回溯到body,body的子节点没有未被遍历的,

linkFns:[(0,div1Link,div1ChildLink),(1,div2Link,div2ChildLink)]

11 在body节点:

linkFns:[0,bodyLink,bodyChildLink]

12 返回能访问body的linkFns的compositeLinkFn闭包函数

收集指令

 collectDirectives函数

参数node为元素节点,是被收集指令的元素节点。参数directives为空数组,用来存放node节点上的各类指令。attrs为Attribute的实例对象,用来记录node节点的属性信息。

node分3种类型查找指令:元素节点,文本节点(即{{}}),注释节点。1.元素节点:在定义指令时restrict对应值为EAC。在第6784行到第6785行,node的标签指令放到数组directives中,restrict对应的值为E。第6786行到6805行,遍历node的属性,把node的属性指令放到数组directives中,restrict对应的值为A。第6806行到第6817行,把node的class指令放到数组directives中,restrict对应的值为C。2.文本节点:如内部指令<div>{{name}}</div>,建立内部指令,监听scope变化而后设置节点的值。3.注释节点。

addDirective函数

上面函数获取指令时,用到addDirective函数,函数以下。

 

编译指令

applyDirectivesToNode函数

收集完某个节点的指令后,使用applyDirectivesToNode函数编译该节点上的指令。

编译的过程:遍历节点的指令数组,依次对素组中指令编译。处理完数组中的指令后,返回闭包函数nodeLinkFn,用于连接过程。

对每一个指令的编译过程:1.判断scope类型。2.判断是否须要controller。3.translude处理。4.template处理。5.异步templateUrl处理。6.存在异步templateUrl的compile函数处理。7.terminal处理。

判断scope类型

判断是否须要controller

transclude处理

template处理


templateUrl处理和非templateUrl状况下,compile处理

收集连接信息,返回连接函数

实例

自定义myDirective指令

使用myDirective指令

显示结果

myDirective编译过程:

1.判断scope

指令的scope设置为一个空对象,而且template为字符串,不是templateUrl。directived对象赋值给newIsolateScopeDirective,directived对象赋值给newScopeDirective。

2.判断controller

指令的controller为一个匿名函数,而且template为字符串,不是templateUrl。设置controllerDirectives.myDirective = directive。

3.处理transclude。tansclude的值为true,获取<myDirective>节点的子节点$template,<myDirective>节点清空,编译$template得到函数childTranscludeFn。

4.处理template,而且restrict为true。模板的第一个元素替换myDirective节点。ng-transclude尚未处理,数据尚未绑定,元素结构和显示结果以下所示:

元素结构:

 

显示结果:

5.处理compile函数。执行compile函数,传入的参数分别为替换myDirective节点的模板节点,属性对象,和第3步得到的函数childTranscludeFn。而后返回连接函数linkFn。而后调用addLinkFns函数,由于newIsolateScopeDirective === directive(由第1步得到),给postLink函数添加隔离做用域标记,而后把返回的连接函数postLink放到数组postLinkFn中。

6.最后,编译完之后,收集用于指令连接的信息,给将要返回的闭包连接函数nodeLinkFn添加一些属性,返回nodeLink连接函数。

 

连接

连接过程为深度优先搜索编译指令的函数。

 

 

 

 

[1] http://liuwanlin.info/angularjsyuan-ma-yue-du-2bian-yi-lian-jie-guo-cheng/

相关文章
相关标签/搜索