要理解angular雙向數據綁定,首先要理解js的事件輪詢---event-loop;html
JavaScript 運行機制詳解:再談Event Loop 這篇文章介紹的不錯;
ajax
理解了以上內容以後就能夠看下面這個文章;
api
下圖與示例描述了Angular如何與瀏覽器事件輪循進行交互。網絡
瀏覽器的事件輪循等待事件到來,事件能夠是用戶交互,定時器事件,或是網絡事件(如 ajax 返回)app
事件發生,其回調被執行,回調的執行就使得應用程序的執行上下文進入到了 JavaScript 的上下文。而後在 JavaScript的上下文中執行,並修改相關的DOM結構異步
一旦回調執行完畢,瀏覽器就離開 JavaScript的上下文回到瀏覽器上下文並基於DOM結構的改變從新渲染視圖async
講了那麼多些,那麼Angular是怎麼在這裏橫插一槓呢?看圖,Angular是插進了 JavaScript的上下文中,經過提供Angular本身的事件處理輪循來改變正常的JavaScript工做流。它實際上是把JavaScript上下文很成了兩塊:一個是傳統的JavaScript執行上下文(圖中淺藍色區域),一個是Angular的執行上下文(圖中淡黃色區域)。 只有在Angular上下文執行的操做纔會受益於Angular的數據綁定,異常處理,屬性檢測,等等。固然,若是不在Angular的上下文中,你也可使用 $apply()
來進入Angular的執行上下文。 須要注意的是,$apply()
在Angular自己的不少地方(如控制器,服務等)都已經被隱式地調用了來處理事件輪循。 顯示地使用 $apply()
只有在你從 JavaScript上下文或是從第三方類庫的回調中想要進入Angular時才須要。讓咱們來看看具體的流程:ide
進入Angular執行上下文的方法,調用 scope.
$apply
(stimulusFn)
。上面 $apply()
中的參數 stimulusFn
是你想要讓它進入Angular上下文的代碼函數
進入 $apply()
以後,Angular執行 stimulusFn()
,而這個函數一般會改變應用程序的狀態(多是數據,或是方法調用等)
以後,Angular進入 $digest
輪循。這個輪循是由兩個較小的輪循構成,一個是處理 $evalAsync
隊列(異步計算的隊列),另外一個是處理 $watch
列表。 $digest
輪循不斷迭代變動(在 $eval
和 $watch
之間變動)直到數據模型穩定,這個狀態其實就是evalAsync
隊列爲空且$watch
列表再也不監測到變化爲止。(譯註:其實這裏就是全部外來的異步操做堆起來成爲一個隊列,由$eval
一個個計算,而後 $watch
看一下這個異步操做對應的數據模型是否還有改變,有改變,就繼續 $eval
這個異步操做,若是沒改變,那就拿異步操做隊列裏的下個異步操做重複上述步驟,直到異步操做隊列爲空以及 $watch
再也不監測到任何數據模型變化爲止)
$evalAsync
隊列是用來安排那些待進入Angular$digest
的異步操做,這些操做每每是在瀏覽器的視圖渲染以前,且經常是經過setTimeout(0)
觸發。可是用 setTimeout(0)
這個方法就不得不承受緩慢遲鈍的響應以及可能引發的閃屏(由於瀏覽器在每次事件發生後都會渲染一次)(譯註:這裏我的以爲不要理解的太複雜,按照上面第三點理解就夠用了,這邊我的翻譯的也不是太好,後期配以例子完善)
$watch
列表則是存放了一組通過 $eval
迭代以後可能會改變的Angular的表達式集合。若是數據模型變化被監測到,那麼 $watch
函數被調用進而用新值更新DOM。
一旦Angular的 $digest
輪循完成,那麼應用程序的執行就會離開Angular及 JavaScript的上下文。而後瀏覽器從新渲染DOM來反映發生的變化
接下來是傳統的 Hello world
示例(就是本節的第一個例子)的流程剖析,這樣你應該就能明白整個例子是如何在用戶輸入時產生雙向綁定的。
編譯階段:
在{{greeting}}
插值(也就是表達式)這裏設置了一個 $watch
來監測 username
的變化
執行階段:
在 <input>
輸入框中按下 'X
' 鍵引發瀏覽器發出一個 keydown
事件
input
指令捕捉到輸入值的改變調用 $apply
("username = 'X';")
進入Angular的執行環境來更新應用的數據模型
Angular將 username = 'X';
做用在數據模型之上,這樣 scope.username
就被賦值爲 'X'
了
$digest
輪循開始
$watch
列表中監測到 username
有一個變化,而後通知 {{greeting}}
插值表達式,進而更新DOM
執行離開Angular的上下文,進而 keydown
事件結束,而後執行也就退出了 JavaScript的上下文;這樣 $digest
完成
瀏覽器用更新了的值從新渲染視圖