AngularJS性能优化总结篇
分类:Javascript| 发布:佚名| 查看:1941 | 发表时间:2014/10/28
本文针对的读者:
- 具备JavaScript性能优化的相关知识(
雅虎14条性能优化原则
、《高性能网站建设指南》
等) - 拥有angular实战经验。
脏数据检查 != 轮询检查更新
谈起angular的脏检查机制(dirty-checking)
, 常见的误解就是认为: ng是定时轮询去检查model是否变更。
其实,ng只有在指定事件触发后,才进入$digest cycle
:
- DOM事件,譬如用户输入文本,点击按钮等。(
ng-click
) - XHR响应事件 (
$http
) - 浏览器Location变更事件 (
$location
) - Timer事件(
$timeout
, $interval
) - 执行
$digest()
或$apply()

参考《mastering web application development with angularjs》 P294
$digest后批量更新UI
传统的JS MVC框架, 数据变更是通过setter去触发事件,然后立即更新UI。
而angular则是进入$digest cycle
,等待所有model都稳定后,才批量一次性更新UI。
这种机制能减少浏览器repaint次数,从而提高性能。
参考《mastering web application development with angularjs》 P296
另, 推荐阅读: 构建自己的AngularJS,第一部分:Scope和Digest
提速 $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~309
1 | var unwatch = $scope.$watch( "someKey" , function (newValue, oldValue){ |
$apply vs $digest
- $apply会使ng进入
$digest cycle
, 并从$rootScope开始遍历(深度优先)检查数据变更。 - $digest仅会检查该scope和它的子scope,当你确定当前操作仅影响它们时,用$digest可以稍微提升性能。
参考《mastering web application development with angularjs》 P308
延迟执行
- 一些不必要的操作,放到
$timeout
里面延迟执行。 - 如果不涉及数据变更,还可以加上第三个参数false,避免调用
$apply
。 - 对时间有要求的,第二个参数可以设置为0。
2 | $scope.name = data.name; |
$evalAsync
vs $timeout
优化ng-repeat
限制列表个数
使用 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为何不考虑此类场景)
如下图,改为bindonce或angular-once后减少了很多:

update:
1.3.0b10开始支持内建单次绑定, {{::variable}}
设计文档:http://t.cn/RvIYHp9
commit: http://t.cn/RvIYHpC
目前该特性的性能似乎还有待优化(2x slower)
更多相关单向绑定的说明:AngularJS1.3中的单向绑定、一次性数据绑定(one-time bindings)
慎用filter
在$digest过程中,filter会执行很多次,至少两次。
所以要避免在filter中执行耗时操作。
参考《mastering web application development with angularjs》 P136
1 | angular.module( 'filtersPerf' , []).filter( 'double' , function (){ |
2 | return function (input) { |
4 | console.log( 'Calling double on: ' +input); |
可以在controller中预先处理
2 | angular.module( 'filtersPerf' , []).controller( 'mainCtrl' , function ($scope, $filter){ |
3 | $scope.dataList = $filter( 'double' )(dataFromServer); |
慎用事件
directive
使用Batarang来分析性能
相关工具
AngularJS在线测试编辑器