天天看點

Angularjs 源碼分析4

ng裡最開始引用<code>$compile</code>的地方就是把所有系統内建的指令添加到<code>$compileprovider</code>裡,由于代碼太長,隻寫些關鍵部分的

此處的<code>directive</code>方法就是<code>$compileprovider</code>裡的<code>registerdirective</code>方法,主要就是把内建指令添加到内部的<code>hasdirectives</code>對象内,以友善後面在全局查找指令的時候進行比對.

啟動的方法在這裡,隻摘取關鍵代碼.

上面的代碼主要作用就是,初始化相關的依賴,然後執行全局編譯,最後更新所有的$watch.

核心的代碼就這一句

其實這裡有兩步

compile(element) 收集完整個頁面内的指令,然後傳回<code>publiclinkfn</code>函數

執行publiclinkfn(scope) 此處的scope即為<code>$rootscope</code>

先來說說第一步

<code>compile</code>服務傳回的是一個構造函數,名為<code>compile</code>,代碼在這裡

從上面的代碼可以看出,如果要查找的節點是文本元素,則包裝一個<code>span</code>标簽,然後執行<code>compilenodes</code>,這個方法主要是收集指令.

上面的編譯節點的主要流程就是,先通過<code>collectdirectives</code>搜集目前節點的指令,然後找到了,則調用<code>applydirectivestonode</code>來應用指令,然後查找目前節點的子節點是否有指令,這是一個遞歸,最後把所有的函數添加到一個内部的<code>linkfns</code>數組中,這個将在最後連結的時候會用到.

先來看看<code>collectdirectives</code>方法,這個方法代碼比較長,就不貼了,直接說代碼邏輯

ng收集指令的時候,首先根據節點類型

element_node 1 先根據tagname來添加指令,然後loop節點的attrs來添加指令,最後通過classname來添加指令

text_node 3 假如是文本節點的話,則調用<code>addtextinterpolatedirective</code>方法來建構指令

像這樣的文本節點就會自動建構上面的指令,自動添加一個監聽,通過修改原生方法來修改節點的值

comment 8 注釋的節點也能自動的添加指令

上面的三種情況的添加指令方法是<code>adddirective</code>

這裡就會用到這個對象<code>hasdirectives</code>,這就是系統在初始化的時候添加的一個内健指令對象集合. 假如節點的名稱在這個對象,則把指令添加到傳遞進來的<code>tdirectives</code>數組内.傳回目前指令.

搜集完指令之後,就要開始使用了,接下來調用<code>applydirectivestonode</code>方法,這個方法将會生成最終連結時候調用的link函數

<code>applydirectivestonode</code>會對<code>directives</code>進行loop,依次檢查指令的屬性,這裡以<code>compile</code>屬性來說,當檢測到指令有<code>compile</code>屬性,則

執行<code>directive.compile</code>方法,傳回一個<code>linkfn</code>,然後調用<code>addlinkfns</code>添加到内部數組中,這裡是<code>postlinkfns</code>數組,最終執行使用者定義的<code>linkfn</code>或者系統自帶的,都會通路這個數組的内容

最後<code>applydirectivestonode</code>傳回的是一個内部函數<code>nodelinkfn</code>,這個就是調用使用者定義指令函數的發起者.

目前節點指令處理完之後,然後開始查找子節點的指令,基本上跟父節點規則一樣,最後傳回<code>compositelinkfn</code>函數給<code>compositelinkfn</code>内部變量,這個下面會用到,最後整個<code>compile</code>函數傳回<code>publiclinkfn</code>函數

到這裡<code>compile(element)</code>就執行完了,再來說說第二步,最終進行指令連結

首先<code>scope</code>是根作用域,這個方法主要是執行所有的連結函數,添加監聽函數.

把目前作用域儲存到元素的data裡,然後調用第一步裡的<code>compositelinkfn</code>函數,傳遞根作用域和根節點

這個會調用<code>compilenodes</code>裡的<code>compositelinkfn</code>方法,此時閉包屬性<code>linkfns</code>屬性裡儲存了兩個項<code>nodelinkfn, childlinkfn</code>,根節點的<code>nodelinkfn</code>為空,<code>childlinkfn</code>有值,它的值本身也是一個<code>compositelinkfn</code>函數,然後傳遞根節點的子節點進去,最終當<code>nodelinkfn</code>有值的時候,會調用<code>applydirectivestonode</code>内部的<code>nodelinkfn</code>方法,上面說了,這個調用所有連結函數的發起者.

<code>nodelinkfn</code>代碼比較長,就不貼了,這裡主要做了以下幾件事

根據指令的scope屬性來建構作用域資訊

是否需要建構控制器,此時會調用控制器的初始化資訊

執行prelinkfns,postlinkfns數組内的連結函數,這些都是在第一步收集好的

核心代碼如下

執行流程為prelinkfns -&gt; childlinkfn -&gt; postlinkfns

最終執行連結的函數在這裡

這裡就會執行使用者自定義的指令内容,以及系統自帶的指令内容,像上面文本節點對應的指令内容,像下面的這個

指令内容裡可以添加監聽,寫一些dom操作的代碼,都是可以的

以上隻是對編譯服務的一些簡單了解,有啥錯誤的希望大家指出來,一起進步,以後有空再分析下業務相關的<code>provider</code>.

繼續閱讀