大家好,我是IT修真院成都分院第3期的學員王奎智,一枚正直純潔善良的WEB前端程序員。
今天給大家分享一下,修真院官網JS任務8,ANGULARJS雙向綁定后,發生了什么事情?是什么可以讓VIEW層和CONTROLLER層進行綁定的?
1.背景介紹
Angular實現了雙向綁定機制
所謂的雙向綁定,無非是從界面的操作能實時反映到數據,數據的變更能實時展現到界面
2.如何綁定
如果讓我們自己實現雙向綁定該怎么寫:
3.如何實現
眾所周知,angular是一個MVVM(Model-View,View-Model)模式的框架,可以實現數據和視圖數據綁定。MVVM把數據加工的任務從Controller中解放了出來。使得Controller只需專注于數據調配的工作。
View是angularjs編譯html后呈現出來的,需要編譯的是controller中的定義的屬性和方法以及directive中定義的指令。
View和controller是獨立開來的,他們之間的紐帶就是圖中間的膠水——scope。Controller負責向scope中提供屬性和方法,便于和view層面的html進行交互。
Angular雙向綁定通過watch,digest和apply實現的。
watch序列
watch監控model中是否有變化,會記錄last值,也就是改變后的值,每一個model都會增加一個watch到watch隊列中。
digest循環
當瀏覽器接收到可以被angular context處理的事件時,digest循環就會觸發,這個循環有兩個子循環,一個處理evalAsync隊列,另一個處理watch隊列,digest會遍歷watch,然后詢問:
既然所有的watch都檢查完了,那就要問了:有沒有watch更新過?如果有至少一個更新過,這個循環就會再次觸發,直到所有的watch都沒有變化。這樣就能夠保證每個model都已經不會再變化。記住如果循環超過10次的話,它將會拋出一個異常,防止無限循環。 當digest循環結束時,DOM相應地變化。
這個就是所謂的dirty-check,angular實際會引入了一個初始值為false的dirty變量作為循環條件,如果有改變過(也就是新舊值不相等),dirty變為true,循環繼續。這里很重要的(也是許多人的很蛋疼的地方)是每一個進入angular context的事件都會執行一個digest循環,也就是說每次我們輸入一個字母循環都會檢查整個頁面的所有watch。
那是什么決定一個事件是否進入angular context呢?
答案是apply
我之前碰到過在angular中使用普通的事件,無法雙向綁定的事情:
這里值雖然改變了,但是沒有強制執行$degest,監視foo的watch根本沒有執行,執行一次apply之后,watch就會知道這些變化,更新dom了。
這里只需要加上scope.$apply()就行了。
一般帶ng的事件angular都會給你自動添加好了apply,所以你要操縱事件的時候加上apply才能實現數據輸出到view層中。
4.常見問題
digest和apply有何區別?干嘛不直接使用digest?
5.解決方法
1.apply可以帶參數,它可以接受一個函數,然后在應用數據之后,調用這個函數。
2.當調用digest的時候,只觸發當前作用域和它的子作用域上的監控,但是當調用apply的時候,會觸發作用域樹上的所有監控。
請看這個例子:
6.擴展思考
7.參考文獻
參考一:Angular中文社區
參考二:杰鍋鍋的博客
8.更多討論
問題一:為什么例五里面自定義指令需要調用$apply方法?是不是自定義指令都不具備$apply方法?
答:不是。不是因為自定義指令本身不具有$apply方法,因為element.on(“click”,function{})這實際上是一個jQuery方法,而jQuery是不具備$apply方法的。如果我們把scope.b++使用一個angular方法去觸發,是不用調用$apply就可以觸發的。
問題二:前文提到的循環十次會拋出異常,是指什么?
是指在一個ng-指令(本身已經自帶$apply方法)內部再次調用$apply方法,就會拋出異常。這實際上是angular的保護機制。
問題三:dirty-checking這么復雜,會不會速度很慢?
不會的,實際上運行的很快。而且在ES6普及后,angular的未來版本會加入Object.observe,$digest循環的速度會更快。
鳴謝
感謝大家觀看
今天的分享就到這里啦,歡迎大家點贊、轉發、留言、拍磚~
你可以直接點擊此鏈接與我一起學習:修真院