AngularJs 性能優化

髒數據檢查 != 輪詢檢查更新
談起angular的 髒檢查機制(dirty-checking) , 常見的誤解就是認爲: ng是定時輪詢去檢查model是否變更。 
其實,ng只有在指定事件觸發後,才進入 $digest cycle :

  • DOM事件,譬如用戶輸入文本,點擊按鈕等。( ng-click )

  • XHR響應事件 ( $http )

  • 瀏覽器Location變更事件 ( $location )

  • Timer事件( $timeout , $interval )

  • 執行 $digest() 或 $apply()

$digest後批量更新UI
傳統的JS MVC框架, 數據變更是通過setter去觸發事件,然後立即更新UI。 
而angular則是進入 $digest cycle ,等待所有model都穩定後,才批量一次性更新UI。 
這種機制能減少瀏覽器repaint次數,從而提高性能。

提速 $digest cycle
關鍵點

盡少的觸發$digest (P310)

儘快的執行$digest

優化$watch
$scope.$watch(watchExpression, modelChangeCallback) , watchExpression可以是String或Function。

避免watchExpression中執行耗時操作 ,因爲它在每次$digest都會執行1~2次。

避免watchExpression中操作dom,因爲它很耗時。

console.log 也很耗時,記得發佈時幹掉它。(用grunt groundskeeper)

ng-if vs ng-show , 前者會移除DOM和對應的watch

及時移除不必要的$watch。 (angular自動生成的可以通過下文介紹的bindonce )

var unwatch = $scope.$watch("someKey", function(newValue, oldValue){  //do sth...
  if(someCondition){    //當不需要的時候,及時移除watch
    unwatch();
  }
});

避免深度watch, 即第三個參數爲true

減少watch的變量長度 
如下,angular不會僅對 {{variable}} 建立watcher,而是對整個p標籤。 
雙括號應該被span包裹,因爲watch的是外部element

<p>plain text other {{variable}} plain text other</p>//改爲:<p>plain text other <span ng-bind='variable'></span> plain text other</p>//或<p>plain text other <span>{{variable}}</span> plain text other</p>

$apply vs $digest
$apply會使ng進入 $digest cycle , 並從$rootScope開始遍歷(深度優先)檢查數據變更。

$digest僅會檢查該scope和它的子scope,當你確定當前操作僅影響它們時,用$digest可以稍微提升性能。

參考《mastering web application development with angularjs》 P308

延遲執行
一些不必要的操作,放到 $timeout 裏面延遲執行。

如果不涉及數據變更,還可以加上第三個參數false,避免調用 $apply 。

對時間有要求的,第二個參數可以設置爲0。

$http.get('http://path/to/url').success(function(data){  
    $scope.name = data.name;  
    $timeout(function(){        //do sth later, such as log
      }, 0, false);
});

$evalAsync vs $timeout

directive中執行的 $evalAsync , 會在angular操作DOM之後,瀏覽器渲染之前執行。

controller中執行的 $evalAsync , 會在angular操作DOM之前執行,一般不這麼用。

而使用 $timeout ,會在瀏覽器渲染之後執行。

優化ng-repeat
限制列表個數

列表對象的數據轉換,在放入scope之前處理。如$scope.dataList = convert(dataFromServer)

可以使用 ngInfiniteScroll 來做無限滾動。

使用 track by

刷新數據時,我們常這麼做: $scope.tasks = data || []; ,這會導致angular移除掉所有的DOM,重新創建和渲染。
 
若優化爲 ng-repeat="task in tasks track by task.id 後,angular就能複用task對應的原DOM進行更新,減少不必要渲染。



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