在文檔加載完(全部資源加載完)之後,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
先判斷編譯的起始節點是不是jqLite包裝的對象,若是不是,把元素包裝成jqLite對象。若是編譯的節點是文本節點,使用<span>對文本節點包裝。 而後使用compileNodes函數開始全局編譯當前節點及其全部子節點,返回當前節點及其子節點的組合的連接函數。最後返回一個閉包函數,用於指令的連接,該函數可以訪問到連接函數compositeLinkFn。
使用深度優先搜索編譯當前節點及其子節點。處理每一個節點的過程:第一步使用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閉包函數
參數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函數,函數以下。
收集完某個節點的指令後,使用applyDirectivesToNode函數編譯該節點上的指令。
編譯的過程:遍歷節點的指令數組,依次對素組中指令編譯。處理完數組中的指令後,返回閉包函數nodeLinkFn,用於連接過程。
對每一個指令的編譯過程:1.判斷scope類型。2.判斷是否須要controller。3.translude處理。4.template處理。5.異步templateUrl處理。6.存在異步templateUrl的compile函數處理。7.terminal處理。
自定義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/