Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

AngularJS性能优化心得 #5

Closed
atian25 opened this issue Nov 28, 2014 · 54 comments
Closed

AngularJS性能优化心得 #5

atian25 opened this issue Nov 28, 2014 · 54 comments
Labels

Comments

@atian25
Copy link
Owner

atian25 commented Nov 28, 2014

不知不觉,在项目中用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()

concepts-runtime

参考《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

var unwatch = $scope.$watch("someKey", function(newValue, oldValue){
  //do sth...
  if(someCondition){
    //当不需要的时候,及时移除watch
    unwatch();
  }
});
  • 避免深度watch, 即第三个参数为true

    参考《mastering web application development with angularjs》 P313

  • 减少watch的变量长度
    如下,angular不会仅对{% raw %}{{variable}}{% endraw %}建立watcher,而是对整个p标签。
    双括号应该被span包裹,因为watch的是外部element

    参考《mastering web application development with angularjs》 P314
    {% raw %}

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

{% endraw %}

$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);
});

优化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行就超标了。
下图这个只是一个很简单的列表,还不是表格,就已经这么多个了:
scope-binding-src
但其实很多属性显示后是几乎不会变更的, 这时候就没必要双向绑定了。(不知道angular为何不考虑此类场景)
如下图,改为bindonceangular-once后减少了很多:
scope-binding-once

update:
1.3.0b10开始支持内建单次绑定, {% raw %}{{::variable}}{% endraw %}
设计文档: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来分析性能

@atian25 atian25 changed the title angular性能优化心得 AngularJS性能优化心得 Dec 1, 2014
@zhangnanMoo
Copy link

赞赞赞!

@daviiz
Copy link

daviiz commented Mar 17, 2015

mark!!

@cpsa3
Copy link

cpsa3 commented Mar 17, 2015

赞,谢谢分享!

@island205
Copy link

@yurychika
Copy link

zan

@vitrum
Copy link

vitrum commented May 15, 2015

马克后看,赞!

@OXOYO
Copy link

OXOYO commented Jun 2, 2015

Nice

@daviiz
Copy link

daviiz commented Jun 10, 2015

mark

2 similar comments
@acs1899
Copy link

acs1899 commented Jul 7, 2015

mark

@andysjt
Copy link

andysjt commented Jul 10, 2015

mark

@zwh8800
Copy link

zwh8800 commented Jul 23, 2015

Nice!

@junstyle
Copy link

mark

2 similar comments
@yyjazsf
Copy link

yyjazsf commented Oct 16, 2015

mark

@qizhiyu
Copy link

qizhiyu commented Nov 8, 2015

mark

@wikieswan
Copy link

@Tomorrow0329
Copy link

Mark!

@yangbai1991
Copy link

赞!

1 similar comment
@Arguseye
Copy link

赞!

@f2eMrWu
Copy link

f2eMrWu commented Apr 25, 2016

新人学到了许多。非常感谢

@junfeisu
Copy link

very good thanks

@kiroInn
Copy link

kiroInn commented May 14, 2016

学习了~

@njleonzhang
Copy link

多谢,很好

@liyj144
Copy link

liyj144 commented Jun 29, 2016

good, mark

@floraluo
Copy link

mark

@atian25
Copy link
Owner Author

atian25 commented Aug 25, 2016

image

@changwei0708 我只能呵呵了... 自己看正文里面的那些图片, 就是我这边九游的应用的截图.

正妹 @RubyLouvre 你这事做的太不地道了吧, 全文转载我的, 还不注明出处.

@diamont1001
Copy link

去博客园举报他

@willworks
Copy link

我想起了当年知乎约战的事情

@atian25
Copy link
Owner Author

atian25 commented Aug 25, 2016

@changwei0708

知道为何很多地方都写着出处么? 因为它来源自我之前在多看上的读书笔记

参考《mastering web application development with angularjs》 P314

image

@zack-lin
Copy link

@RubyLouvre 怎么看?

1 similar comment
@kinglion
Copy link

@RubyLouvre 怎么看?

@releasethecow
Copy link

@RubyLouvre 怎么看?

@changwei0708
Copy link

@atian25 看到楼上的@我就放心了

@changwei0708
Copy link

@diamont1001 @willworks 不要啊,闹着玩的

@menglexing
Copy link

不嫌事大。。。

@zhanglun
Copy link

zhanglun commented Aug 25, 2016

image
看不太懂了。。这个发布时间,,,

感觉有好戏了 😄

@atian25
Copy link
Owner Author

atian25 commented Aug 25, 2016

@RubyLouvre
Copy link

什么事,我从是一个到处飘着广告的网站中搬过来的。不知原作者啊。

@menglexing
Copy link

恩,我相信 @RubyLouvre 这点素质还是有的

@RubyLouvre
Copy link

我可以将你这个地址 加到我博文中

@atian25
Copy link
Owner Author

atian25 commented Aug 25, 2016

@RubyLouvre 好吧~ 注明下吧, 不然哪天我又被你的粉丝纠正了~

@ngot
Copy link

ngot commented Aug 25, 2016

原来是偷的别人的赃物。。。

@Justineo
Copy link

原来不知原作者就可以当做自己的东西发布了,厉害。这个借口可以用一辈子。

@hacke2
Copy link

hacke2 commented Aug 25, 2016

我觉的就算是到处飘着广告的网站也还是得贴上那个网站地址。。

@djyde
Copy link

djyde commented Aug 25, 2016

我不太明白为什么明明是转载也不标明非原创。。。

@fengjx
Copy link

fengjx commented Aug 25, 2016

mark

@shauvet
Copy link

shauvet commented Mar 26, 2017

受教了,还是感谢下。。。

ps:下面一堆大神出没。。。

@HughDai
Copy link

HughDai commented May 26, 2017

mark

@baishusama
Copy link

baishusama commented Aug 19, 2017

比较好奇是哪三本书。。

@atian25
Copy link
Owner Author

atian25 commented Aug 19, 2017 via email

@dengnan123
Copy link

现在呢 老哥 继续用的1吗 还是2 或者4 了

@atian25
Copy link
Owner Author

atian25 commented Oct 21, 2017 via email

@XJawher
Copy link

XJawher commented Nov 14, 2017

Vue 文档确实很棒

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

No branches or pull requests