angular性能优化心得

不知不觉,在项目中用angular已经半年多了,踩了很多坑。 趁着放假,把angular的3本书都看了遍,结合这半年的经验,是该做个总结了。 希望可以给大家带来启示,少踩点坑。
本文针对的读者:

  • 具备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

console.log也很耗时,记得发布时干掉它。(用grunt groundskeeper)

ng-if vs ng-show

前者会移除DOM和对应的watch

及时移除不必要的$watch。

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

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

避免深度watch, 即第三个参数为true

参考《mastering web application development with angularjs》 P313

减少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>

参考《mastering web application development with angularjs》 P314

$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

http://stackoverflow.com/questions/17301572/angularjs-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进行更新,减少不必要渲染。

参见: http://www.codelord.net/2014/04/15/improving-ng-repeat-performance-with-track-by

  • 使用bindonce减少绑定
    我们都知道angular建议一个页面最多2000个双向绑定,但在列表页面通常很容易超标。 譬如一个滑动到底部加载下页的表格,一行20+个绑定, 展示个100行就超标了。 但其实很多属性显示后是几乎不会变更的, 这时候就没必要双向绑定了。

慎用filter

在$digest过程中,filter会执行很多次,至少两次。 所以要避免在filter中执行耗时操作 。

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

可以在controller中预先处理

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

参考《mastering web application development with angularjs》 P136

慎用事件

减少事件广播,使用双向数据绑定或共享service等方法来代替。
$broadcast会遍历scope和它的子scope,而不是只通知注册了该事件的子scope。一个优化方式是使用 $emit.

参见 https://github.com/angular/angular.js/issues/4574

1.2.7版本对事件做过一个优化,

参见 https://github.com/angular/angular.js/blob/master/CHANGELOG.md#127-emoji-clairvoyance-2014-01-03

对高频的事件做缓冲限速,避免触发太频繁。

directive

跟scope数据无关的操作放在compile阶段,它只执行一次。
除了directive外其他地方,特别是controller里面不要操作dom, 尤其是绑定到scope后,便是灾难。改变以前使用JQuery那样 以DOM为中心的思维,拥抱以数据为中心的思维。

参见: http://stackoverflow.com/questions/14994391/how-do-i-think-in-angularjs-if-i-have-a-jquery-background
翻译: http://blog.jobbole.com/46589/

使用Batarang来分析性能

AngularJS Batarang 是官方提供的chrome插件

搬运自: http://atian25.github.io/2014/05/09/angular-performace/

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 206,968评论 6 482
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 88,601评论 2 382
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 153,220评论 0 344
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 55,416评论 1 279
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 64,425评论 5 374
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,144评论 1 285
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,432评论 3 401
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,088评论 0 261
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 43,586评论 1 300
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,028评论 2 325
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,137评论 1 334
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,783评论 4 324
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,343评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,333评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,559评论 1 262
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,595评论 2 355
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,901评论 2 345

推荐阅读更多精彩内容