dragos rusu - angular js - overcoming performance issues - codecamp-10-may-2014

74
AngularJS. Performance & Limits. Dragos Rusu - CodeCamp Iasi 2014 ([email protected])

Upload: codecampiasi

Post on 29-Nov-2014

112 views

Category:

Software


1 download

DESCRIPTION

Dragos Rusu - Angular JS - Overcoming Performance Issues - CodeCamp-10-may-2014

TRANSCRIPT

Page 1: Dragos Rusu  - Angular JS - Overcoming Performance Issues - CodeCamp-10-may-2014

AngularJS.Performance &

Limits.Dragos Rusu - CodeCamp Iasi 2014

([email protected])

Page 2: Dragos Rusu  - Angular JS - Overcoming Performance Issues - CodeCamp-10-may-2014

Our story

From"We have to rethink this whole module, remove time navigation... it's just too sluggish." ★

to"It's awesome, really fast, it's like going from night to day!" ★

(★) SOFTVISION customer

Page 3: Dragos Rusu  - Angular JS - Overcoming Performance Issues - CodeCamp-10-may-2014

A few words about me

Dragos Rusu @ SOFTVISION

WEB/ZEND ENGINEER since 2007 (backend, frontend)

ARTICLE WRITER (PHP Architect)

PROJECTS: platforms for airlines (Cathay Pacific, Singapore Airlines, Air Berlin), tourism agencies,home automation and security, agriculture

Page 4: Dragos Rusu  - Angular JS - Overcoming Performance Issues - CodeCamp-10-may-2014

We will discuss about...

1. View watches / data bindings2. What you see is what you show3. The risk of polluting scopes4. Core directives to avoid5. Splitting the page6. Miscellaneous7. Limits

Q / A

Page 5: Dragos Rusu  - Angular JS - Overcoming Performance Issues - CodeCamp-10-may-2014

disclaimer

PERFORMANCE principles for heavy apps (+500 man days)

* many items are not covered here.

* code samples - only in AngularJS

never used AngularJS before? no problem, principles are general, yet the solutions are particular.

Page 6: Dragos Rusu  - Angular JS - Overcoming Performance Issues - CodeCamp-10-may-2014

Shall we?

Page 7: Dragos Rusu  - Angular JS - Overcoming Performance Issues - CodeCamp-10-may-2014

1. View watches / data bindings

Page 8: Dragos Rusu  - Angular JS - Overcoming Performance Issues - CodeCamp-10-may-2014

GENERAL CONTEXT

"more of 2000 watchers can lag the UI" (angular-tips.com)"the expressions in curly braces denote bindings" ({{ ... }}) (docs.angularjs.org)

"AngularJS internally creates a $watch for each ng-* directive" (github.com/Pasvaz/bindonce)"ngRepeat directive instantiates a template once per item [...] each template instance gets its own

scope" (docs.angularjs.org)

Page 9: Dragos Rusu  - Angular JS - Overcoming Performance Issues - CodeCamp-10-may-2014

Ok... but why would I be counting watches?

Page 10: Dragos Rusu  - Angular JS - Overcoming Performance Issues - CodeCamp-10-may-2014
Page 11: Dragos Rusu  - Angular JS - Overcoming Performance Issues - CodeCamp-10-may-2014

Every watcher is run at the digest cycle. The digest cycle is repeated until none of the results haschanged value

(Brian Ford - AngularJS contributor)

Page 12: Dragos Rusu  - Angular JS - Overcoming Performance Issues - CodeCamp-10-may-2014

✈ A peak into AngularJS source code

$apply: function(expr) { try { beginPhase('$apply'); return this.$eval(expr); } catch (e) { $exceptionHandler(e); } finally { clearPhase(); try { $rootScope.$digest(); // Ouh my...// [...]

?

https://github.com/angular/angular.js/blob/master/src/ng/rootScope.js#L943

Page 13: Dragos Rusu  - Angular JS - Overcoming Performance Issues - CodeCamp-10-may-2014
Page 14: Dragos Rusu  - Angular JS - Overcoming Performance Issues - CodeCamp-10-may-2014
Page 15: Dragos Rusu  - Angular JS - Overcoming Performance Issues - CodeCamp-10-may-2014
Page 16: Dragos Rusu  - Angular JS - Overcoming Performance Issues - CodeCamp-10-may-2014
Page 17: Dragos Rusu  - Angular JS - Overcoming Performance Issues - CodeCamp-10-may-2014

(1) double-binding creates tons of listeners

Page 18: Dragos Rusu  - Angular JS - Overcoming Performance Issues - CodeCamp-10-may-2014

SOLUTION: Whenever feasible, use single-binding solutions

double binding DEMO / bindonce DEMO

Page 19: Dragos Rusu  - Angular JS - Overcoming Performance Issues - CodeCamp-10-may-2014

Why? Is double-binding slow?

Not quite. The AngularJS way of implementing it is slow ($dirty flags instead of observableproperties).

pssst: ECMA6: Object.observe()

Page 20: Dragos Rusu  - Angular JS - Overcoming Performance Issues - CodeCamp-10-may-2014

github.com/Pasvaz/bindonce

github.com/kseamon/fast-bind

pssst: bindonce will be part of AngularJS 1.3 release!

Page 21: Dragos Rusu  - Angular JS - Overcoming Performance Issues - CodeCamp-10-may-2014

(2) direct function calls from templates are called very oftenSOLUTION: pre-compute the values to shown in the view model (default values, totals, etc)

<ul> <li ng-repeat="item in items"> // {{ item.name }} ({{ computeTotal(item) }}) {{ item.name }} ({{ item.computedTotal }}) </li></ul>

?

Page 22: Dragos Rusu  - Angular JS - Overcoming Performance Issues - CodeCamp-10-may-2014

EXAMPLE

vatTotal will be recomputed on each scope $digest, regardless if the values that vatTotal() depend onare changed or not (DEMO)

// ... controller ...$scope.vat = 24; // %$scope.vatTotal = function () { return ( $scope.data.item.total * (1 + $scope.vat / 100) );};// ... template ...{{ vatTotal() }}

?

Page 23: Dragos Rusu  - Angular JS - Overcoming Performance Issues - CodeCamp-10-may-2014

(3) iterating over large data sets slows down the pageSOLUTION: create a lightweight iterable view model

angular.module('codecamp').controller('testCtrl', [ '$scope', 'dm', '$q', function ($scope, dm, $q) { 'use strict'; $scope._init = function () { $scope.testViewModel = {}; $q.all([dm.getData1(), dm.getData2()]) .then(function (response) { $scope.computeViewModel(response.data); } ); }; $scope._init(); });

?

Page 24: Dragos Rusu  - Angular JS - Overcoming Performance Issues - CodeCamp-10-may-2014

(4) ng-repeat extra DOM manipulationsSOLUTION: ng-repeat with track by id (DEMO)

<ul> <li ng-repeat="item in items track by item.id"> {{ item.name }} </li></ul>

?

Page 25: Dragos Rusu  - Angular JS - Overcoming Performance Issues - CodeCamp-10-may-2014

(5) filters are called very oftenSOLUTION: lightweight quasi-independent filters

<span>{{ value }}</span><ul> <li ng-repeat="item in items"> {{ item.name |heavyFilter item.value, $index }} </li></ul>

?

WARNING: avoid touching DOM in filters and watches

Page 26: Dragos Rusu  - Angular JS - Overcoming Performance Issues - CodeCamp-10-may-2014

(6) multiple recursive $watch might cause page flickeringSOLUTION: try to avoid recursive watch, where feasible

$scope.$watch('model.items', function (newValue, oldValue) { // do smth }, recursive = true););

?

Page 27: Dragos Rusu  - Angular JS - Overcoming Performance Issues - CodeCamp-10-may-2014

(7) direct DOM watch functions might slow down the pageSOLUTION: try to avoid complex valueExpression, where feasible (use the data model instead)

// Directive LINK functionlink: function ($scope, $el, $attrs) { $scope.$watch( function () { return $el[0].childNodes.length; }, function (newValue, oldValue) {} );}

?

SOURCE: stackoverflow.com/questions/21332671

Page 28: Dragos Rusu  - Angular JS - Overcoming Performance Issues - CodeCamp-10-may-2014

1. View watches / data bindings2. What you see is what you show3. The risk of polluting scopes4. Core directives to avoid5. Splitting the page6. Miscellaneous7. Limits

Page 29: Dragos Rusu  - Angular JS - Overcoming Performance Issues - CodeCamp-10-may-2014

2. What you see is what you show

Page 30: Dragos Rusu  - Angular JS - Overcoming Performance Issues - CodeCamp-10-may-2014

ng-if vs. ng-show (1)

ng-hide and ng-show makes no speed difference (DEMO)

<ul ng-hide="hideCondition"> <li ng-repeat="item in items"> {{ item.value }} </li></ul>

?

Page 31: Dragos Rusu  - Angular JS - Overcoming Performance Issues - CodeCamp-10-may-2014

ng-if vs. ng-show (2)

ng-if/ng-switch might make a difference on more content(e.g. tabbed page)

<ul ng-if="displayCondition"> // CONTENT</ul>

?

* fewer bindings* fewer linkers called at startup

Page 32: Dragos Rusu  - Angular JS - Overcoming Performance Issues - CodeCamp-10-may-2014

remove non-visible elements in the scroll (1)

one easy way would be PAGINATIONdoesn't always apply though...

Page 33: Dragos Rusu  - Angular JS - Overcoming Performance Issues - CodeCamp-10-may-2014

remove non-visible elements in the scroll (2)

DISPLAY elements, but ONLY THE VISIBLE ones

known as the VIRTUAL/INFINITE SCROLLING problem

Page 34: Dragos Rusu  - Angular JS - Overcoming Performance Issues - CodeCamp-10-may-2014

remove non-visible elements in the scroll (3)

usually occurs when large data sets need to be displayed

OpenSource solutions:http://binarymuse.github.io/ngInfiniteScroll/

http://blog.stackfull.com/2013/02/AngularJS-virtual-scrolling-part-1/

DEMO / DEMO (with virtual scroll)

Page 35: Dragos Rusu  - Angular JS - Overcoming Performance Issues - CodeCamp-10-may-2014

1. View watches / data bindings2. What you see is what you show3. The risk of polluting scopes4. Core directives to avoid5. Splitting the page6. Miscellaneous7. Limits

Page 36: Dragos Rusu  - Angular JS - Overcoming Performance Issues - CodeCamp-10-may-2014

3. The risk of polluting scopes

Page 37: Dragos Rusu  - Angular JS - Overcoming Performance Issues - CodeCamp-10-may-2014

✈ DOM / SCOPES

Page 38: Dragos Rusu  - Angular JS - Overcoming Performance Issues - CodeCamp-10-may-2014
Page 39: Dragos Rusu  - Angular JS - Overcoming Performance Issues - CodeCamp-10-may-2014
Page 40: Dragos Rusu  - Angular JS - Overcoming Performance Issues - CodeCamp-10-may-2014

(1) relying on multiple $rootScope and appCtrl functions may slow down the $digestSOLUTION: try to avoid polluting $rootScope/appCtrl/* scopes

angular.module('codecamp').service('appInit', ['$rootScope', function ($rootScope) { 'use strict'; $rootScope.computeStuff = function () { ... }; $rootScope.getData = function () { ... }; $rootScope.i18n = function () { ... }; $rootScope.manageAppStates = function () { ... }; $rootScope.manageFormatters = function () { ... }; // ... and so forth});

?

- dispatch to specialized services/factories/filters/*

Page 41: Dragos Rusu  - Angular JS - Overcoming Performance Issues - CodeCamp-10-may-2014

... and have some privacy in all scopes

angular.module('codecamp').controller('testCtrl', ['$scope', function ($scope) { 'use strict'; $scope._privateMethod = function () {}; function notRecommended () { // ... }});

?

Page 42: Dragos Rusu  - Angular JS - Overcoming Performance Issues - CodeCamp-10-may-2014

1. View watches / data bindings2. What you see is what you show3. The risk of polluting scopes4. Core directives to avoid5. Splitting the page6. Miscellaneous7. Limits

Page 43: Dragos Rusu  - Angular JS - Overcoming Performance Issues - CodeCamp-10-may-2014

4. Core directives to avoid

Page 44: Dragos Rusu  - Angular JS - Overcoming Performance Issues - CodeCamp-10-may-2014

ALL of them

... right

Page 45: Dragos Rusu  - Angular JS - Overcoming Performance Issues - CodeCamp-10-may-2014

Remember what we've said earlier?

"more of 2000 watchers can lag the UI" (angular-tips.com)

Page 46: Dragos Rusu  - Angular JS - Overcoming Performance Issues - CodeCamp-10-may-2014

var ngEventDirectives = {}; forEach('click dblclick mousedown mouseup mouseover blur mouseout mousemove mouseenter mouseleave copy keydown keyup keypress submit focus cut paste' .split(' '), function(name) { // [...] return function(scope, element, attr) { //LINK element.on(lowercase(name), function(event) { // scope.$apply => $rootScope.$apply scope.$apply(function() { fn(scope, {$event:event}); }); // [...]

?

SOURCE: github.com/angular/angular.js/blob/master/src/ng/directive/ngEventDirs.js#L41

Page 47: Dragos Rusu  - Angular JS - Overcoming Performance Issues - CodeCamp-10-may-2014
Page 48: Dragos Rusu  - Angular JS - Overcoming Performance Issues - CodeCamp-10-may-2014

Does that really matter?

video not displayableQuad core, 8 GB of RAM, Win7

Page 49: Dragos Rusu  - Angular JS - Overcoming Performance Issues - CodeCamp-10-may-2014

Seems so...

video not displayable

Page 50: Dragos Rusu  - Angular JS - Overcoming Performance Issues - CodeCamp-10-may-2014

SOLUTION: write custom directive(s), catch the events you need and...

.directive('customMouseEnter',[function () { 'use strict'; return { restrict: 'A', link: function (scope, elem, attrs) { var fName = attrs.customMouseEnter, func = function (ev) { scope[fName](ev); }; elem.on('mouseenter', func); scope.$on('$destroy', function () { elem.off('mouseenter', func); });// [...]

?

Page 51: Dragos Rusu  - Angular JS - Overcoming Performance Issues - CodeCamp-10-may-2014

...trigger local $digests (DEMO $digest over $apply)

// TEMPLATE<tr custom-mouse-enter="ctrlMouseLeave"> // CONTROLLER / DIRECTIVEscope.mouseLeave = function (ev) { // highlight, etc // $digest() only on the scope you need scope.$digest();};

?

Page 52: Dragos Rusu  - Angular JS - Overcoming Performance Issues - CodeCamp-10-may-2014

Seems to be pretty common in the community

http://stackoverflow.com/questions/18421732/angularjs-how-to-override-directive-ngclickhttp://briantford.com/blog/angular-hacking-core

(★) if you actually override default directives, remember to set a higher priority

Page 53: Dragos Rusu  - Angular JS - Overcoming Performance Issues - CodeCamp-10-may-2014

1. View watches / data bindings2. What you see is what you show3. The risk of polluting scopes4. Core directives to avoid5. Splitting the page6. Miscellaneous7. Limits

Page 54: Dragos Rusu  - Angular JS - Overcoming Performance Issues - CodeCamp-10-may-2014

5. Splitting the page

Page 55: Dragos Rusu  - Angular JS - Overcoming Performance Issues - CodeCamp-10-may-2014

identify what is shareable and what is not

avoid splitting the page in too many sub-components

design your components in a blackbox manner

Page 56: Dragos Rusu  - Angular JS - Overcoming Performance Issues - CodeCamp-10-may-2014

1. View watches / data bindings2. What you see is what you show3. The risk of polluting scopes4. Core directives to avoid5. Splitting the page6. Miscellaneous7. Limits

Page 57: Dragos Rusu  - Angular JS - Overcoming Performance Issues - CodeCamp-10-may-2014

6. Miscenallaneous

Page 58: Dragos Rusu  - Angular JS - Overcoming Performance Issues - CodeCamp-10-may-2014

(1) evalAsync(f) over $timeout(f)

More: http://www.bennadel.com/blog/2605-scope-evalasync-vs-timeout-in-angularjs.htm

Page 59: Dragos Rusu  - Angular JS - Overcoming Performance Issues - CodeCamp-10-may-2014

(2) Watch out for external components performance and their usage

We had a problem with Moment.JS library (20% of the page load time, according to ChromeProfiler)

Page 60: Dragos Rusu  - Angular JS - Overcoming Performance Issues - CodeCamp-10-may-2014

(3) $eval your code from time to time - PERF wise

Batarang (identify $watchers), Chrome Profiler (memory, performance), performance.now()

Page 61: Dragos Rusu  - Angular JS - Overcoming Performance Issues - CodeCamp-10-may-2014

(BONUS) demythify events

Page 62: Dragos Rusu  - Angular JS - Overcoming Performance Issues - CodeCamp-10-may-2014

$emit / $broadcast (1)

SOURCE: jsperf.com/rootscope-emit-vs-rootscope-broadcast/24

Page 63: Dragos Rusu  - Angular JS - Overcoming Performance Issues - CodeCamp-10-may-2014

$emit / $broadcast (2)

SOURCE: jsperf.com/rootscope-emit-vs-rootscope-broadcast/25

Page 64: Dragos Rusu  - Angular JS - Overcoming Performance Issues - CodeCamp-10-may-2014

Ok... but why?

AngularJS 1.2.6 and below ▶ 12-15x differenceAngularJS 1.2.7+ ▶ 1.1x difference

"limit propagation of $broadcast to scopes that have listeners for the event"(github.com/angular/angular.js/blob/master/CHANGELOG.md#performance-improvements-3)

RECAP: Common sense still says we should use them according to their design

Page 65: Dragos Rusu  - Angular JS - Overcoming Performance Issues - CodeCamp-10-may-2014

1. View watches / data bindings2. What you see is what you show3. The risk of polluting scopes4. Core directives to avoid5. Splitting the page6. Miscellaneous7. Limits

Page 66: Dragos Rusu  - Angular JS - Overcoming Performance Issues - CodeCamp-10-may-2014

7. Limits

Page 67: Dragos Rusu  - Angular JS - Overcoming Performance Issues - CodeCamp-10-may-2014

"you quickly reach the end of what Angular can do for you when it comes to structuringapplications, at which point the community fragments transform to best practices, and few people

have figured out how to write large-scale Angular apps" (EmberJS core member)

Page 68: Dragos Rusu  - Angular JS - Overcoming Performance Issues - CodeCamp-10-may-2014

Technical limits

1. + 2.000 dynamic elements on the screen2. + 3.000 watchers3. real time apps, where data changes very often

(★) depending on the device

Page 69: Dragos Rusu  - Angular JS - Overcoming Performance Issues - CodeCamp-10-may-2014

Some apps examples:

stocks exchangegoogle mapsoffice apps

OUTPUT: screen flickering, low UX, unresponsive screens

And there's nothing you can do about it... except rewriting it in a lightweight framework

Page 70: Dragos Rusu  - Angular JS - Overcoming Performance Issues - CodeCamp-10-may-2014

1. View watches / data bindings2. What you see is what you show3. The risk of polluting scopes4. Core directives to avoid5. Splitting the page6. Miscellaneous7. Limits

Page 71: Dragos Rusu  - Angular JS - Overcoming Performance Issues - CodeCamp-10-may-2014

Recap? (1)

(1) be aware of too many data bindings (bindonce)

(2) try to minimize the number of $digest cycles

(3) have pre-computed values at template level

(4) display only the visible elements (virtual scroll)

Page 72: Dragos Rusu  - Angular JS - Overcoming Performance Issues - CodeCamp-10-may-2014

Recap? (2)

(5) be aware of core directives PERF problems

(6) don't pollute your scopes and make them TDD friendly

(7) watch out for external components (angular or non-angular) performance

Page 73: Dragos Rusu  - Angular JS - Overcoming Performance Issues - CodeCamp-10-may-2014

What's next?

it's a good habit to think PERF (pre-$compile the code in your head)don't assume the frameworks are fast, whatever you may write

watch out for memory leaks

REMINDER: AngularJS is quite easy, just try it!... and last but not least important: find a company that would allow you to grow your skills!

Page 74: Dragos Rusu  - Angular JS - Overcoming Performance Issues - CodeCamp-10-may-2014

Q / A