Slide 1

Slide 1 text

No content

Slide 2

Slide 2 text

AngularJS the performance parts @toddmotto

Slide 3

Slide 3 text

» Lead Engineer @ Mozio » Google Developer Expert » @toddmotto » Blog at toddmotto.com

Slide 4

Slide 4 text

Topics » New features (1.2 - 1.3+) » Generic changes » Perf features » Performance driven Angular » $digest loop/$$watchers/$$asyncQueue » Quick wins, tips and tricks » Structure practices, advanced techniques

Slide 5

Slide 5 text

1.2 to 1.3+

Slide 6

Slide 6 text

1.2 to 1.3+ generic changes

Slide 7

Slide 7 text

» IE8 support dropped » DOM manipulation » ~4.3 times faster » 73% less garbage » $digest loop » ~3.5 times faster » 78% less garbage » 400+ bug fixes

Slide 8

Slide 8 text

1.2 to 1.3+ perf features

Slide 9

Slide 9 text

» One-time bind syntax » ngModelOptions » bindToController property » ngModel.$validators » ngMessage/ngMessages » strictDI » $applyAsync in $http » Disable debug info

Slide 10

Slide 10 text

one time bindings

{{ ::vm.name }}

  • {{ ::user.name }}

Slide 11

Slide 11 text

one time bindings » Defined with :: » $watched until not "undefined" » $$watcher is unbound » Will not update upon Model changes » One-time, not one-way » Great for single static rendering

Slide 12

Slide 12 text

ng-Model-Options

Slide 13

Slide 13 text

ng-Model-Options

Slide 14

Slide 14 text

ng-Model-Options // directive controller function FooDirCtrl() { // undo model changes if (condition) { this.model.$rollbackViewValue(); } }

Slide 15

Slide 15 text

ng-Model-Options » Fine tune how Model updates are done » Define event types » Add debounce to delay Model synchronisation » e.g. { debounce: 250 } = $digest ~250ms » $rollbackViewValue for undoing model changes

Slide 16

Slide 16 text

bindToController // directive controller function FooDirCtrl() { this.bar = {}; this.doSomething = function doSomething(arg) { this.bar.foobar = arg; }.bind(this); }

Slide 17

Slide 17 text

bindToController // directive controller function FooDirCtrl($scope) { this.bar = {}; this.doSomething = function doSomething(arg) { this.bar.foobar = arg; // reference the isolate property $scope.name = arg.prop; }.bind(this); }

Slide 18

Slide 18 text

bindToController function fooDirective() { return { ... scope: {}, bindToController: { name: '=' }, ... }; }

Slide 19

Slide 19 text

bindToController // directive controller function FooDirCtrl() { this.bar = {}; this.doSomething = function doSomething(arg) { this.bar.foobar = arg; // reference the isolate property this.name = arg.prop; }.bind(this); }

Slide 20

Slide 20 text

bindToController » Used with "controllerAs" (class-like) » Binds isolate props to the Controller instance » No $scope » $scope remains "special use only" » Not used for data » Used for $watch/$on/etc

Slide 21

Slide 21 text

ngModel.$validators // old school function visaValidator() { var VISA_REGEXP = /^4[0-9]{12}(?:[0-9]{3})?$/; function link($scope, element, attrs, ngModel) { ngModel.$parsers.unshift(function (value) { var valid = VISA_REGEXP.test(value); ngModel.$setValidity('visaValidator', valid); return valid ? value : undefined; }); } return { require : 'ngModel', link : link }; } angular.module('app').directive('visaValidator', visaValidator);

Slide 22

Slide 22 text

ngModel.$validators // new school function visaValidator() { var VISA_REGEXP = /^4[0-9]{12}(?:[0-9]{3})?$/; function link($scope, element, attrs, ngModel) { ngModel.$validators.visaValidator = function (value) { return VISA_REGEXP.test(value); // Boolean }; } return { require : 'ngModel', link : link }; } angular.module('app').directive('visaValidator', visaValidator);

Slide 23

Slide 23 text

ngModel.$validators
Not a valid VISA format!

Slide 24

Slide 24 text

ngModel.$validators » ngModel.$validators Object » Instead of $parsers/$formatters » Return a Boolean from the bound function » Use with the $error Object in the View

Slide 25

Slide 25 text

ngMessage/ngMessages Enter email:
You did not enter a field
Your email must be between 5 and 100 characters long

Slide 26

Slide 26 text

ngMessage/ngMessages » Conditional validation » ngModel.$error Object » Acts like a switch case

Slide 27

Slide 27 text

strictDI

Slide 28

Slide 28 text

strictDI // implicit annotation function SomeService($scope, $timeout) { //... } angular .module('app') .factory('SomeService', SomeService);

Slide 29

Slide 29 text

strictDI function SomeService($scope, $timeout) { //... } // Array annotations SomeService.$inject = ['$scope', '$timeout']; angular .module('app') .factory('SomeService', SomeService);

Slide 30

Slide 30 text

strictDI » Runs the application's $injector in strict mode » Throws an error on Services using implicit annotations » Use ng-annotate to automate this process

Slide 31

Slide 31 text

$applyAsync with $http function config($httpProvider) { $httpProvider.useApplyAsync(true); } angular .module('app', []) .config(config); More: blog.thoughtram.io/angularjs/2015/01/14/exploring-angular-1.3-speed-up-with-applyAsync.html

Slide 32

Slide 32 text

$applyAsync with $http » Enables $applyAsync to be used with $http » Schedules an async $apply for batched requests » For requests that resolve within ~10ms » Pushes into $$asyncQueue » Single $digest

Slide 33

Slide 33 text

Disable debug info function config($compileProvider) { $compileProvider.debugInfoEnabled(false); } angular .module('app', []) .config(config);

Slide 34

Slide 34 text

Disable debug info
// content
// content

Slide 35

Slide 35 text

Disable debug info » Disable in production for performance boosts » Removes $scope references on elements » Doesn't add classes to DOM nodes with binding info » Enable in console with angular.reloadWithDebugInfo();

Slide 36

Slide 36 text

Performance driven Angular

Slide 37

Slide 37 text

Understand what impacts performance before you code

Slide 38

Slide 38 text

Under the hood: $digest

Slide 39

Slide 39 text

$digest fundamentals » $digest loop » $$watchers ($watch) » $$asyncQueue ($evalAsync)

Slide 40

Slide 40 text

No content

Slide 41

Slide 41 text

No content

Slide 42

Slide 42 text

No content

Slide 43

Slide 43 text

No content

Slide 44

Slide 44 text

No content

Slide 45

Slide 45 text

No content

Slide 46

Slide 46 text

No content

Slide 47

Slide 47 text

No content

Slide 48

Slide 48 text

$digest: $digest loop » Triggered by $scope.$apply / built-in events » Iterates $$watchers Array on $scope » If model value is different from last calculated then corresponding listener executes » Exits loop, Angular loops again (10 max) » Repaints DOM (View expressions updated)

Slide 49

Slide 49 text

$digest: $$watchers » View events/bindings {{ foo }} » Angular adds a watch to the $watch list » Only $watched if bound in the View » Dirty checked in the $digest loop » Minimise use of $$watchers / avoid if possible

Slide 50

Slide 50 text

$digest: $$asyncQueue » $evalAsync » Runs first in $digest » May run $digest again to flush $$asyncQueue

Slide 51

Slide 51 text

track by Scenarios: * Large DOM lists * Slow DOM updates * $digests blocking UI thread (lagging)

Slide 52

Slide 52 text

track by
  • {{ user.name }}

Slide 53

Slide 53 text

track by
  • {{ user.name }}

Slide 54

Slide 54 text

track by » Minimal DOM repaints (only what's changed) » Uses Object references instead of Angular hashes

Slide 55

Slide 55 text

ng-if / switch vs ng-show / hide

Slide 56

Slide 56 text

ng-if / switch vs ng-show / hide » ng-if/switch reconstruct the DOM » ng-if/switch for less frequent/heavier rendering » ng-show/hide toggle "ng-hide" class » ng-show/hide for more frequent/lighter rendering » ng-show/hide less performant due to $$watchers (when hidden)

Slide 57

Slide 57 text

ng-bind over {{ handlebars }}

{{ vm.username }}

Welcome to Facebook

Slide 58

Slide 58 text

ng-bind over {{ handlebars }} » No DOM flicker (invisible bindings) with ng-bind » Significantly faster » ng-perf.com/2014/10/30/tip-4-ng-bind-is-faster- than-expression-bind-and-one-time-bind » Lesser need for ng-cloak » Angular won't evaluate entire text content

Slide 59

Slide 59 text

$apply or $digest? // forces a $rootScope.$digest(); $scope.$apply(); // forces a [current $scope].$digest(); $scope.$digest();

Slide 60

Slide 60 text

$apply or $digest? » $scope certainties » Prevent a full $rootScope.$digest() if you're certain only child $scopes need updating » Improve performance by not forcing a full $rootScope.$digest » $scope.$digest runs on current and child $scopes » $scope.$apply triggers $rootScope.$digest call

Slide 61

Slide 61 text

$destroy unbinding function myFunction () { // handle element clicks } // bind element.on('click', myFunction); // unbind $scope.$on('$destroy', function () { element.off('click', myFunction); });

Slide 62

Slide 62 text

$destroy unbinding » Remove event listeners that may cause memory leaks » DOM nodes that are destroyed » Manually unbind by listening to $destroy » $scope.$on events are automatically removed

Slide 63

Slide 63 text

Deep $watch vs $watchCollection var prop = [{...},{...},{...},{...}]; $scope.$watch('prop', function (newValue, oldValue) { }, true); $scope.$watchCollection('prop', function (newValue, oldValue) { });

Slide 64

Slide 64 text

Deep $watch vs $watchCollection » Deep $watch uses deep Object tree comparison (expensive) » $watchCollection goes only one level deep » Shallow reference of all top level items » Try not to use either unless you have to » Not as testable » Signs of bad architecture » Litter Controllers

Slide 65

Slide 65 text

avoiding DOM filters

{{ vm.date | date: 'dd-MM-yyyy' }}

Slide 66

Slide 66 text

avoiding DOM filters function SomeCtrl($filter) { // date passed in elsewhere var time = 1444175093303; // Parsed in JS before bound to the DOM this.parsedDate = $filter('date')(time, 'dd-MM-yyyy'); } angular .module('app') .controller('SomeCtrl', SomeCtrl);

Slide 67

Slide 67 text

avoiding DOM filters

{{ vm.parsedDate }}

Slide 68

Slide 68 text

avoiding DOM filters » DOM filters run twice per $digest » Preprocess in a Controller » Bind parsed value to the View

Slide 69

Slide 69 text

Takeaways » Understand the $digest loop » Investigate the performance side of each Directive/API you use » Consider using JavaScript over DOM bindings where possible ($filter etc.) » Check Angular's GitHub repo for changelogs/ releases

Slide 70

Slide 70 text

Thank you @toddmotto speakerdeck.com/toddmotto

Slide 71

Slide 71 text

No content