转:AngularJS性能优化总结篇
2016-07-11 09:42
483 查看
AngularJS性能优化总结篇本文针对的读者:具备JavaScript性能优化的相关知识(
避免深度watch, 即第三个参数为true参考《mastering web application development with angularjs》 P313减少watch的变量长度如下,angular不会仅对
可以在controller中预先处理
雅虎14条性能优化原则、
《高性能网站建设指南》等)拥有angular实战经验。Contents1. 脏数据检查!= 轮询检查更新2. $digest后批量更新UI3. 提速$digest cycle3.1. 关键点3.2. 优化$watch3.3. $applyvs $digest3.4. 延迟执行4. 优化ng-repeat4.1. 限制列表个数4.2. 使用track by5. 使用单次绑定6. 慎用filter7. 慎用事件8. directive9. 使用Batarang来分析性能
脏数据检查 != 轮询检查更新
谈起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也很耗时,记得发布时干掉它。(用gruntgroundskeeper)
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){ |
2 | //do sth... |
3 | if (someCondition){ |
4 | //当不需要的时候,及时移除watch |
5 | unwatch(); |
6 | } |
7 | }); |
{{variable}}建立watcher,而是对整个p标签。双括号应该被span包裹,因为watch的是外部element参考《mastering web application development with angularjs》 P314
1 | < p >plaintext other {{variable}} plain text other</ p > |
2 | //改为: |
3 | < p >plaintext other < span ng-bind = 'variable' ></ span >plain text other</ p > |
4 | //或 |
5 | < p >plaintext other < span >{{variable}}</ span >plain text other</ p > |
$apply vs $digest
$apply会使ng进入$digestcycle,并从$rootScope开始遍历(深度优先)检查数据变更。$digest仅会检查该scope和它的子scope,当你确定当前操作仅影响它们时,用$digest可以稍微提升性能。参考《mastering web application development with angularjs》 P308
延迟执行
一些不必要的操作,放到$timeout里面延迟执行。如果不涉及数据变更,还可以加上第三个参数false,避免调用
$apply。对时间有要求的,第二个参数可以设置为0。
1 | $http.get( 'http://path/to/url' ).success( function (data){ |
2 | $scope.name= data.name; |
3 | $timeout( function (){ |
4 | //dosth later,such as log |
5 | },0, false ); |
6 | }); |
$evalAsyncvs
$timeouthttp://stackoverflow.com/questions/17301572/angularjs-evalasync-vs-timeoutdirective中执行的
$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 taskstrack 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/RvIYHp9commit: http://t.cn/RvIYHpC目前该特性的性能似乎还有待优化(2x slower)更多相关单向绑定的说明:AngularJS1.3中的单向绑定、一次性数据绑定(one-timebindings)
慎用filter
在$digest过程中,filter会执行很多次,至少两次。所以要避免在filter中执行耗时操作。参考《mastering web application development with angularjs》 P1361 | angular.module( 'filtersPerf' ,[]).filter( 'double' , function (){ |
2 | return function (input){ |
3 | //至少输出两次 |
4 | console.log( 'Callingdouble on: ' +input); |
5 | return input+ input; |
6 | }; |
7 | }); |
1 | //mainCtrl.js |
2 | angular.module( 'filtersPerf' ,[]).controller( 'mainCtrl' , function ($scope,$filter){ |
3 | $scope.dataList= $filter( 'double' )(dataFromServer); |
4 | }); |
慎用事件
减少事件广播,使用双向数据绑定或共享service等方法来代替。$broadcast会遍历scope和它的子scope,而不是只通知注册了该事件的子scope。一个优化方式是使用
$emit,参见https://github.com/angular/angular.js/issues/45741.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来分析性能
AngularJSBatarang是官方提供的chrome插件相关工具
相关文章推荐
- Angularjs 跨域请求
- AngularJS基础教程之简单介绍
- AngularJS中处理多个promise的方式
- AngularJS入门(用ng-repeat指令实现循环输出
- angularJS 中$attrs方法使用指南
- AngularJS实现textarea记录只能输入规定数量的字符并显示
- 深入浅析AngularJS和DataModel
- 简述AngularJS相关的一些编程思想
- 不能不知道的10个angularjs英文学习网站
- AngularJS中的$watch(),$digest()和$apply()区分
- Angularjs中的事件广播 ―全面解析$broadcast,$emit,$on
- AngularJS的一些基本样式初窥
- AngularJS 如何在控制台进行错误调试
- AngularJS使用指令增强标准表单元素功能
- 简单讲解AngularJS的Routing路由的定义与使用
- 创建你的第一个AngularJS应用的方法
- Angularjs过滤器使用详解
- 详解AngularJS中的作用域
- Angular发布1.5正式版,专注于向Angular 2的过渡
- 简介AngularJS的视图功能应用