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/

相關文章
相關標籤/搜索