雙向綁定是AngularJS核心概念之一,它給我們帶來了思維的轉變,不再是以DOM為驅動,而是以Model為核心,View中寫上聲明式標簽(指令或{{}}),AngularJS會在后臺默默同步View到Model,并將Model的變化更新到View。其雖然帶來了極大好處,但是需要有一種有性能隱患的“臟檢查機制”,隨時觀察所有綁定值的變化,如果這些需要觀察的值太多,就會產生性能問題。
一、watchers函數
AngularJS利用$watch API來監控$scope上Model的變化,AngularJS應用在編譯模版的時候,會收集模版上的聲明式標簽-指令或表達式,并鏈接它們,這個過程指令或表達式會注冊自己的監控函數,即watchers函數。
有個AngularJS表達式{{}},其會在其所在的$scope中注冊watchers函數(watch是監控mode變化,observe是監控DOM中屬性變化),監控Mode中的count屬性的變化,以便能更新到view。每次點擊Button時候,count計數器加1,觸發digest過程將變化同步到View上。這里是單向更新從Model更新到View上。如果界面上有個帶有ngModel指令的input控件,View上每次輸入都會被及時更新到Model上。
Model上的數據被更新到了View,背后是臟檢查機制被觸發,它會執行當前$scope及其所有子$scope上注冊的watcher函數,如果變化了就執行相應處理函數,直到Model穩定了,如果這個過程中Model發生了變化,瀏覽器會重新渲染DOM來反應Model變化。
另外,AngularJS會在編譯階段手機所有指令,表達式{{}}會被解析成一種特殊的指令:addTextInterpolateDirective;在link階段,就會利用$watch的API來注冊watchers函數,所以,表達式也會成為digest循環中watchers的一員。
不僅Angular表達式,其他的指令如ngShow、ngHide和ngBind,都會通過$watch API添加watchers函數。ngBind需要一個HTML節點,并以attribute屬性標記的方式。
所以,如果有太多watchers函數,那么在每次digest循環時候,肯定會慢下來,這是臟檢查機制的性能瓶頸,超過2000個watchers,就會感到明顯卡頓,所以,減少$watch,去掉不必要的$watch。在開發時候,盡量減少顯式使用$Scope.$watch,AngularJS很多內置的指令已經滿足大部分的業務需求,如ngChange,ngClick,不需要額外添加$watch。
二、one-time綁定
如果sessions有300個,那么這個循環會產生300*5+1(ngRepeat本身)個$watch,每次點擊likeSession時候,會檢查name,room等5個屬性是否會變化,而其實這5個屬性一旦綁定是不會變化的,沒必要watch,這個時候可以使用one-time綁定。
AngularJS的單次綁定在1.3版本引入,單次表達式在第一次digest后,將不再計算(檢測屬性變化),寫法是在表達式前邊加個“:”前綴,如下:
? ? ?
? ?三、滾屏加載
一種可行的性能解決方案,用于大量數據集顯示的時候,又不想分頁,所以一般放在頁面底部,當滾動屏幕到達底部時候,就嘗試加載一個序列的數據集,追加到底部。可以用開源組件ngInfiniteScroll。