走進AngularJs(四)自定義指令----(中)

原文轉自:http://www.cnblogs.com/lvdabao/p/3398044.html

上一篇簡單介紹了自定義一個指令的幾個簡單參數,restrict、template、templateUrl、replace、transclude,這幾個理解起來相對容易很多,因爲它們只涉及到了表現,而沒有涉及行爲。這一篇將繼續學習ng自定義指令的幾個重量級參數,瞭解了它們之後我們的custom directive將不光能“看”,還要能“動”。開始~

理解compile和link

  不知大家有沒有這樣的感覺,自己定義指令的時候跟寫jQuery插件有幾分相似之處,都是先預先定義好頁面結構及監聽函數,然後在某個元素上調用一下,該元素便擁有了特殊的功能。區別在於,jQuery的側重點是DOM操作,而ng的指令中除了可以進行DOM操作外,更注重的是數據和模板的綁定。jQuery插件在調用的時候纔開始初始化,而ng指令在頁面加載進來的時候就被編譯服務($compile)初始化好了。

  在指令定義對象中,有compile和link兩個參數,它們是做什麼的呢?從字面意義上看,編譯、鏈接,貌似太抽象了點。其實可大有內涵,爲了在自定義指令的時候能正確使用它們,現在有必要了解一下ng是如何編譯指令的。上一篇我有列了一下指令的執行流程,但僅僅列1234有點太對不起觀衆了,故在此詳細分析一下。此知識點相當重要。

指令的解析流程詳解

  我們知道ng框架會在頁面載入完畢的時候,根據ng-app劃定的作用域來調用$compile服務進行編譯,這個$compile就像一個大總管一樣,清點作用域內的DOM元素,看看哪些元素上使用了指令(如<div ng-modle=”m”></div>),或者哪些元素本身就是個指令(如<mydierc></mydirec>),或者使用了插值指令( {{}}也是一種指令,叫interpolation directive),$compile大總管會把清點好的財產做一個清單,然後根據這些指令的優先級(priority)排列一下,真是個細心的大總管哈~大總管還會根據指令中的配置參數(template,place,transclude等)轉換DOM,讓指令“初具人形”。

  然後就開始按順序執行各指令的compile函數,注意此處的compile可不是大總管$compile,人家帶着$是土豪,此處執行的compile函數是我們指令中配置的,compile函數中可以訪問到DOM節點並進行操作,其主要職責就是進行DOM轉換,每個compile函數執行完後都會返回一個link函數,這些link函數會被大總管匯合一下組合成一個合體後的link函數,爲了好理解,我們可以把它想象成葫蘆小金剛,就像是進行了這樣的處理

複製代碼
//合體後的link函數
function AB(){
  A(); //子link函數
  B(); //子link函數
}
複製代碼

  接下來進入link階段,合體後的link函數被執行。所謂的鏈接,就是把view和scope鏈接起來。鏈接成啥樣呢?就是我們熟悉的數據綁定,通過在DOM上註冊監聽器來動態修改scope中的數據,或者是使用$watchs監聽 scope中的變量來修改DOM,從而建立雙向綁定。由此也可以斷定,葫蘆小金剛可以訪問到scope和DOM節點。

  不要忘了我們在定義指令中還配置着一個link參數呢,這麼多link千萬別搞混了。那這個link函數是幹嘛的呢,我們不是有葫蘆小金剛了嘛?那我告訴你,其實它是一個小三。此話怎講?compile函數執行後返回link函數,但若沒有配置compile函數呢?葫蘆小金剛自然就不存在了。正房不在了,當然就輪到小三出馬了,大總管$compile就把這裏的link函數拿來執行。這就意味着,配置的link函數也可以訪問到scope以及DOM節點。值得注意的是,compile函數通常是不會被配置的,因爲我們定義一個指令的時候,大部分情況不會通過編程的方式進行DOM操作,而更多的是進行監聽器的註冊、數據的綁定。所以,小三名正言順的被大總管寵愛~

  聽完了大總管、葫蘆小金剛和小三的故事,你是不是對指令的解析過程比較清晰了呢?不過細細推敲,你可能還是會覺得情節生硬,有些細節似乎還是沒有透徹的明白,所以還需要再理解下面的知識點:

compile和link的區別

  其實在我看完官方文檔後就一直有疑問,爲什麼監聽器、數據綁定不能放在compile函數中,而偏偏要放在link函數中?爲什麼有了compile還需要link?就跟你質疑我編的故事一樣,爲什麼最後小三被寵愛了?所以我們有必要探究一下,compile和link之間到底有什麼區別。好,正房與小三的PK現在開始。

         首先是性能。舉個例子:

複製代碼
<ul>
  <li ng-repeat="a in array">
    <input ng-modle=”a.m” />
  </li>
</ul>
複製代碼

         我們的觀察目標是ng-repeat指令。假設一個前提是不存在link。大總管$compile在編譯這段代碼時,會查找到ng-repeat,然後執行它的compile函數,compile函數根據array的長度複製出n個<li>標籤。而複製出的<li>節點中還有<input>節點並且使用了ng-modle指令,所以compile還要掃描它並匹配指令,然後綁定監聽器。每次循環都做如此多的工作。而更加糟糕的一點是,我們會在程序中向array中添加元素,此時頁面上會實時更新DOM,每次有新元素進來,compile函數都把上面的步驟再走一遍,豈不是要累死了,這樣性能必然不行。

         現在扔掉那個假設,在編譯的時候compile就只管生成DOM的事,碰到需要綁定監聽器的地方先存着,有幾個存幾個,最後把它們彙總成一個link函數,然後一併執行。這樣就輕鬆多了,compile只需要執行一次,性能自然提升。

         另外一個區別是能力。儘管compile和link所做的事情差不多,但它們的能力範圍還是不一樣的。比如正房能管你的存款,小三就不能。小三能給你初戀的感覺,正房卻不能。

         我們需要看一下compile函數和link函數的定義:

function compile(tElement, tAttrs, transclude) { ... }

function link(scope, iElement, iAttrs, controller) { ... }   

         這些參數都是通過依賴注入而得到的,可以按需聲明使用。從名字也容易看出,兩個函數各自的職責是什麼,compile可以拿到transclude,允許你自己編程管理乾坤大挪移的行爲。而link中可以拿到scope和controller,可以與scope進行數據綁定,與其他指令進行通信。兩者雖然都可以拿到element,但是還是有區別的,看到各自的前綴了吧?compile拿到的是編譯前的,是從template裏拿過來的,而link拿到的是編譯後的,已經與作用域建立了關聯,這也正是link中可以進行數據綁定的原因。

  正房與小三的區別就是性能和能力兩個關鍵字,簡記爲性能力,我想你永遠都不會忘記了吧,真相就是如此的赤裸裸啊~哈哈

  我暫時只能理解到這個程度了。實在不想理解這些知識的話,只要簡單記住一個原則就行了:如果指令只進行DOM的修改,不進行數據綁定,那麼配置在compile函數中,如果指令要進行數據綁定,那麼配置在link函數中

無奈的結束

  理解指令的處理流程以及compile和link的區別,花費了我大量的時間。資料太少了,官方文檔翻來覆去看,最後理解到了這個程度,但總覺得還是差那麼一點,沒有100%理解到位。今天太困了,就記錄到此處吧,以後有了新的理解再做補充。

  其實這篇的標題我想寫(下)的,直接把scope、require、controller一併介紹完畢,現在看來是不可能了,因爲這幾個參數也是需要重點理解的,這樣下去篇幅又hold不住了。這裏就當是預報好了。。。囧。。。以前覺得沒有附圖和代碼示例的博客不是好博客,現在反倒覺得要碼出這麼多字來,也是需要下功夫的。


發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章