angular性能優化心得

原文出處

髒數據檢查 != 輪詢檢查更新

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

  • DOM事件,譬如用戶輸入文本,點擊按鈕等。(ng-click)
  • XHR響應事件 ($http)
  • 瀏覽器Location變動事件 ($location)
  • Timer事件($timeout, $interval)
  • 執行$digest()$apply()

參考《mastering web application development with angularjs》 P294java

$digest後批量更新UI

傳統的JS MVC框架, 數據變動是經過setter去觸發事件,而後當即更新UI。
而angular則是進入$digest cycle,等待全部model都穩定後,才批量一次性更新UI。
這種機制能減小瀏覽器repaint次數,從而提升性能。jquery

參考《mastering web application development with angularjs》 P296
另, 推薦閱讀: 構建本身的AngularJS,第一部分:Scope和Digestgit

提速 $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

    參考《mastering web application development with angularjs》 P303~309angularjs

var unwatch = $scope.$watch("someKey", function(newValue, oldValue){
  //do sth...
  if(someCondition){
    //當不須要的時候,及時移除watch
    unwatch();
  }
});
  • 避免深度watch, 即第三個參數爲truegithub

    參考《mastering web application development with angularjs》 P313web

  • 減小watch的變量長度
    以下,angular不會僅對{{variable}}創建watcher,而是對整個p標籤。
    雙括號應該被span包裹,由於watch的是外部elementchrome

    參考《mastering web application development with angularjs》 P314瀏覽器

<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》 P308app

延遲執行

  • 一些沒必要要的操做,放到$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);
});

優化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進行更新,減小沒必要要渲染。
參見:http://www.codelord.net/2014/04/15/improving-ng-repeat-performance-with-track-by

使用單次綁定

咱們都知道angular建議一個頁面最多2000個雙向綁定,但在列表頁面一般很容易超標。
譬如一個滑動到底部加載下頁的表格,一行20+個綁定, 展現個100行就超標了。
下圖這個只是一個很簡單的列表,還不是表格,就已經這麼多個了:

但其實不少屬性顯示後是幾乎不會變動的, 這時候就不必雙向綁定了。(不知道angular爲什麼不考慮此類場景)
以下圖,改成bindonceangular-once後減小了不少:

update:
1.3.0b10開始支持內建單次綁定, {{::variable}}
設計文檔:http://t.cn/RvIYHp9
commit: http://t.cn/RvIYHpC
目前該特性的性能彷佛還有待優化(2x slower)

慎用filter

在$digest過程當中,filter會執行不少次,至少兩次。
因此要避免在filter中執行耗時操做

參考《mastering web application development with angularjs》 P136

angular.module('filtersPerf', []).filter('double', function(){
  return function(input) {
    //至少輸出兩次
    console.log('Calling double on: '+input);
    return input + input;
  };
});

能夠在controller中預先處理

//mainCtrl.js
angular.module('filtersPerf', []).controller('mainCtrl', function($scope, $filter){
  $scope.dataList = $filter('double')(dataFromServer);
});

慎用事件

directive

使用Batarang來分析性能

相關文章
相關標籤/搜索