angular源码分析5-编译链接

 

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

编译链接

编译链接架构

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

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

 3.编译

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

使用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,全局指令链接。

 

编译过程

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/

posted @ 2017-06-22 21:28  springmin  阅读(221)  评论(0编辑  收藏  举报