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来分析性能

相关文章
相关标签/搜索