angular 雙向數據綁定原理

要理解angular雙向數據綁定,首先要理解js的事件輪詢---event-loop;html

JavaScript 運行機制詳解:再談Event Loop 這篇文章介紹的不錯;
ajax

理解了以上內容以後就能夠看下面這個文章;
api

angular何爲做用域(Scope)
瀏覽器

與瀏覽器事件輪循整合

下圖與示例描述了Angular如何與瀏覽器事件輪循進行交互。網絡

  1. 瀏覽器的事件輪循等待事件到來,事件能夠是用戶交互,定時器事件,或是網絡事件(如 ajax 返回)app

  2. 事件發生,其回調被執行,回調的執行就使得應用程序的執行上下文進入到了 JavaScript 的上下文。而後在 JavaScript的上下文中執行,並修改相關的DOM結構異步

  3. 一旦回調執行完畢,瀏覽器就離開 JavaScript的上下文回到瀏覽器上下文並基於DOM結構的改變從新渲染視圖async

講了那麼多些,那麼Angular是怎麼在這裏橫插一槓呢?看圖,Angular是插進了 JavaScript的上下文中,經過提供Angular本身的事件處理輪循來改變正常的JavaScript工做流。它實際上是把JavaScript上下文很成了兩塊:一個是傳統的JavaScript執行上下文(圖中淺藍色區域),一個是Angular的執行上下文(圖中淡黃色區域)。 只有在Angular上下文執行的操做纔會受益於Angular的數據綁定,異常處理,屬性檢測,等等。固然,若是不在Angular的上下文中,你也可使用 $apply() 來進入Angular的執行上下文。 須要注意的是,$apply() 在Angular自己的不少地方(如控制器,服務等)都已經被隱式地調用了來處理事件輪循。 顯示地使用 $apply() 只有在你從 JavaScript上下文或是從第三方類庫的回調中想要進入Angular時才須要。讓咱們來看看具體的流程:ide

  1. 進入Angular執行上下文的方法,調用 scope.$apply(stimulusFn) 。上面 $apply() 中的參數 stimulusFn 是你想要讓它進入Angular上下文的代碼函數

  2. 進入 $apply() 以後,Angular執行 stimulusFn() ,而這個函數一般會改變應用程序的狀態(多是數據,或是方法調用等)

  3. 以後,Angular進入 $digest 輪循。這個輪循是由兩個較小的輪循構成,一個是處理 $evalAsync 隊列(異步計算的隊列),另外一個是處理 $watch 列表。 $digest 輪循不斷迭代變動(在 $eval 和 $watch 之間變動)直到數據模型穩定,這個狀態其實就是evalAsync 隊列爲空且$watch 列表再也不監測到變化爲止。(譯註:其實這裏就是全部外來的異步操做堆起來成爲一個隊列,由$eval一個個計算,而後 $watch 看一下這個異步操做對應的數據模型是否還有改變,有改變,就繼續 $eval 這個異步操做,若是沒改變,那就拿異步操做隊列裏的下個異步操做重複上述步驟,直到異步操做隊列爲空以及 $watch 再也不監測到任何數據模型變化爲止)

  4. $evalAsync 隊列是用來安排那些待進入Angular$digest 的異步操做,這些操做每每是在瀏覽器的視圖渲染以前,且經常是經過setTimeout(0) 觸發。可是用 setTimeout(0) 這個方法就不得不承受緩慢遲鈍的響應以及可能引發的閃屏(由於瀏覽器在每次事件發生後都會渲染一次)(譯註:這裏我的以爲不要理解的太複雜,按照上面第三點理解就夠用了,這邊我的翻譯的也不是太好,後期配以例子完善)

  5. $watch 列表則是存放了一組通過 $eval 迭代以後可能會改變的Angular的表達式集合。若是數據模型變化被監測到,那麼 $watch 函數被調用進而用新值更新DOM。

  6. 一旦Angular的 $digest 輪循完成,那麼應用程序的執行就會離開Angular及 JavaScript的上下文。而後瀏覽器從新渲染DOM來反映發生的變化

接下來是傳統的 Hello world 示例(就是本節的第一個例子)的流程剖析,這樣你應該就能明白整個例子是如何在用戶輸入時產生雙向綁定的。

  1. 編譯階段:

    1. ng-model 和 input 指令 在 <input> 標籤中設置了一個 keydown 監聽器

    2. {{greeting}} 插值(也就是表達式)這裏設置了一個 $watch 來監測 username 的變化

  2. 執行階段:

    1. 在 <input> 輸入框中按下 'X' 鍵引發瀏覽器發出一個 keydown 事件

    2. input 指令捕捉到輸入值的改變調用 $apply("username = 'X';") 進入Angular的執行環境來更新應用的數據模型

    3. Angular將 username = 'X'; 做用在數據模型之上,這樣 scope.username 就被賦值爲 'X' 了

    4. $digest 輪循開始

    5. $watch 列表中監測到 username 有一個變化,而後通知 {{greeting}} 插值表達式,進而更新DOM

    6. 執行離開Angular的上下文,進而 keydown 事件結束,而後執行也就退出了 JavaScript的上下文;這樣 $digest 完成

    7. 瀏覽器用更新了的值從新渲染視圖

相關文章
相關標籤/搜索