zoukankan      html  css  js  c++  java
  • 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/

  • 相关阅读:
    死锁
    钩子函数和回调函数的区别
    蓝绿部署、滚动发布、灰度发布的介绍以及最佳实践
    小公司的瓶颈
    Modbus协议详解
    windows+jenkin
    Java:简单的多态实例
    一、Kubernetes系列之介绍篇
    Shell脚本自动搭建ipsec环境
    Appium(1):安卓自动化环境搭建 + Android SDK + Appium 环境搭建
  • 原文地址:https://www.cnblogs.com/fe-huahai/p/7067298.html
Copyright © 2011-2022 走看看