Angular JS

Performance



goo.gl/9ylsTB



www.thatJSDude.com / @mdkhan005

(youtube.com/user/khanLearning)

Angular app

Build one


  • Create input
  • show two way bindings
  • using ng-model and expression



angular js pitfalls

Magic

Two phases

  • Compilation phase
  • Runtime phase

Compilation Phase


<input ng-model="name">
						
  1. input with ng-model becomes angular directive
  2. Setup a keydown listener to input
  3. 
    <h1>{{name}}</h1>
    						
  4. Something in angular named: $interpolation
  5. interpolation: markup with expression => string
  6. interpolation setup a $watch in current scope
  7. $watch will be notified if "name" changes


$compile vs $interpolate vs $parse

Runtime Phase

  1. When you press 'X'
  2. Browser emits a keydown event on input
  3. input directive captures change
  4. calls $apply("name = 'X';") to update model
  5. Angular applies the name = 'X'; to the model
  6. The $digest loop begins
  7. $watch list detects change notifies $interpolation
  8. $interpolation updates the DOM by $parse
  9. angular finishes keydown event
  10. browser render new text


scope performance considerations

Scope

Scopes

  • JavaScript Object
  • rootscope is created during app bootstrap
  • Prototypical Inheritance
  • Arranged in hierarchical structure
  • Mimic the DOM structure of the application
  • Stores an array of functions $$watchers
  • Can watch expressions and propagate events.
  • new scope creates more values for GC to collect later


scope and
understanding scope

What can be done

  • small scope will be faster than larger
  • $destroy()- Removes the current scope (and all of its children)
  • $destroy - makes scope eligible for GC


Large angular app performance

ng-show

how it works


<div ng-show="myValue"></div>
						
  • show or hide html element
  • based on an expression
  • by adding and removing .ng-hide class
  • will create and evaluate hidden elements
  • display none


start from here

ng-if


<span ng-if="checked">
  This is removed when the checkbox is unchecked.
</span>
						
  • completely removes and recreate DOM
  • elements which is not in ur DOM, has less impact
  • ng-if creates a child scope while ng-show/ng-hide does not
  • ng-switch does the same thing

What should i use?



  • How frequently will this change?
  • the more frequent, the worse fit ng-if is
  • How heavy is the scope?
  • the heavyer, the better fit ng-if is

filter

how often filter executes?



{{ filter_expression | filter : expression : comparator}}
						
  • Filters are really simple to use
  • We insert a pipe, the filter name and we’re done
  • Every single filter twice per $digest cycle
  • First run is from the $$watchers detecting any changes
  • Second run is to see if there are further changes that need updated values



show filter demo


limit DOM filter

What should I do

  • Filter data early instead of template filters when possible

{{ filter_expression | filter : expression : comparator }}
						

$filter('filter')(array, expression, comparator);
						


how often filter executes?

ng repeat

how ng-repat works

  1. Iterates over each item
  2. Runs via filters, if any
  3. Calculates a hash value to identify the object has already created a DOM
  4. If so, it reuses it
  5. If not, creates DOM based on ng-repeat template
  6. Insert into the actual DOM
  7. A watch is added on the array
  8. watch triggers step 1 again if the array undergoes any change


two ng repeat improvement suggestion

ng-include in ng-repeat

  • Each time -
  • clone ngInlcude directive to run a link function
  • injects content
  • calls $compile
  • html in the ng-include needs to be parse again and again


ng repeat with nginclude

browser will spend time to parse html for each item

What can i do?


<li ng-repeat="item in filteredItems()"> 
						
 
<li ng-repeat="item in items">
						

what can i do?

  • Pagination
  • limitTo

<table>
    <tr ng-repeat="d in data | limitTo:totalDisplayed">
        <td>{{d}}</td>
   </tr>
</table>
						
  • debounce user input (searching)


  • tuning long list

    ng Infinite Scroll


    • don't load everything use ng infinite scroll



    read about ngInfiniteScroll

    track by

  • By default, ng-repeat creates a dom node for each item and destroys that dom node when the item is removed.
  • With track by $index, it will reuse dom nodes.
  • 
    <div ng-repeat=”item in array”>
      I live and die by {{item}}.
    <div>
    						
    
    <div ng-repeat=”item in array track by item.Id”>
      I live until {{array.length}} is
      less than {{$index}}.
    <div>
    						

    ng-repeat performance by track by

    bind once

    git: bindonce

    summary ng repeat

    • Do not evaluate function
    • reduce filter
    • pagination
    • limitTo
    • track by
    • bind once
    • infinite scroll

    binding

    ng-bind vs {{}}

    • ng-bind is faster because it’s simpler in implementation
    • {{}} has to go through extra steps of verifying context, jsonification of values and more


    ng-bind is faster than {{}}

    {{}} time

    one time binding

    
     <p>One time binding {{::name}}</p>
     <p>Normal binding {{name}}</p>
    						
    • dont watch after first bind
    • Reducing the number of expressions being watched
    • digest loop gets faster
    • if value is undefined, keep watching
    • after getting a value, watcher will be deregistered



    one time binding

    when to use it

    • interpolating text or attributes:
    • 
      <div name="attr: {{::color}}">text: {{::name}}</div>
      						
    • a directive with bidirectional binding and the parameters will not change:
    • 
      <div some-directive name="::myName" color="My color is {{::myColor}}"></div>
      							
    • a directive that takes an expression:
    • 
      <ul>
        <li ng-repeat="item in ::items">{{item.name}};</li>
      </ul>
      						

    Digest cycle

    digest cycle


    • any changes via view/service input
    • will trigger a $digest cycle
    • More data bindings creates
    • more watchers and scope objects
    • will take longer on each digest cycle



    read section 9

    debounce digest

    
    <input ng-model="name" ng-model-options="{ debounce: 250 }" />
    						

  • show it in demo


  • delay digest cycle

    Outside Changes

    $apply

    • a Model change has occurred outside of angular
    • $scope.$apply to inform Angular about change
    • use it correctly, in the past and thrown uncaught errors in my JavaScrip
    • $scope.$apply kicks off the $digest loop
    • digest loop in the rootscope
    • this means everything: dirty checked will run
    • Everything means: Entire application: all of the watchers
    • 
      $(elem).myPlugin({
        onchange: function (newValue) {
          // model changes outside of Angular
          $(this).val(newValue);
          // tell Angular values have changed and to update via $digest
          $scope.$apply();
        }
      });
      							


    code example in the middle

    use scope.$digest for performance

    • $apply - trigger watchers on the entire $scope chain
    • $digest() method will only trigger watchers on the current $scope and its children
    • Show demo | digest.html
    • Only problem: if you have anything on the parent, that will not get updated


    scope.digest as perf imp

    watcher

    how angular updates

    • remembers the value and compares it to previous value
    • basic dirty-checking
    • If there is a change in value, then it fires the change event.
    • not everything in scope gets watch

    Implicit watcher

    • {{myExpression}}
    • ng-repeat
    • ng-show, ng-hide
    • ng-if, ng-switch


    A function gets added to the $$watchers array

    explicit watcher

    
    $watch(watchExpression, listener, [objectEquality]);
    						
    • will notify changes
    • update changes to the DOM
    • whatever is the first argument will be executed, many many times
    • it will compare values by reference
    • but if the third argument is "true"
    • deep - recurse through object with deep comparison
    • slow recurses- make a deep copy each time watched, to compare


    avoid deep watch each time possible

    watchCollection

    
    $watchCollection(obj, listener);
    						

    watch performance consideration

    count watcher

    use this explanation style

    nice explanation
    count number of watch

    do as little as possible

    • Avoid binding function directly to ng-show, ng-repeat, etc.
    • Avoid watch a function result directly
    • This function will run on every digest cycle
    • will slow your application


    Misko answer

    aggregate watcher



    • log viewer word watch
    • visit link below



    optimization 2: 1200ms-to-35ms

    remove unneeded watcher


    
    var unbinder = scope.$watch('scopeValueToBeWatcher', function(newVal, oldVal){
    	//do something
    });
    
    
    unbinder(); //this line removes the watch from $$watchers.
    						

    check this one

    watch only what needed

  • sometimes, deep watch is needed but not the whole object
  • strip out irrelevant data to make it faster.
  • 
    $scope.$watch(‘listOfBigObjects’, myHandler, true);
    						
    
    $scope.$watch(function($scope) {
      return $scope.listOfBigObjects.
          map(function(bigObject) {
            return bigObject.foo.fieldICareAbout;
          });
    }, myHandler, true);
    						

    directive

    directive execution


    • when directive inside ng repeat
    • compile is called only once
    • link and constructor is called once per iteration
    
    myApp.directive(function() {
      return {
        link: function(s, elem, attr) {
          s.foo = scope.$eval(attr.foo);
          scope.$watch(attr.list, function(list) {
                // do something
              }, true);
        }};
    });
    						
    
    myApp.directive(function($parse) {
      return {
        compile: function(elem, attr) {
          var fooExp = $parse(attr.foo),
              listExp = $parse(attr.list);
          return function link(s, elem) {
            s.foo = fooExp(s);
            scope.$watchCollection(listExp, function(list) {
                  // do something
            });
          };
        }
      };
    });
    
    						

    so put as much as possible inside compile

    Extra

    broadcast vs emit



    • $rootScope.$emit - only $rootScope listeners. don't want every $scope to get it
    • $rootScope.$broadcast - every scope hear it
    • $scope.$emit - scope and all parents including rootscope hear it
    • $scope.$broadcast - itself and children


    talk about emit, broadcast and bootstrap

    performance talk

    • $parse vs $eval vs interpolation
    • parse: tokenise
    • better: call parse once. save it
    • and do eval everytime u needed



    ng conf video on performance

    ng-class vs ng-style

    • ng-class demo
    • ng-style demo
    • delay is due to Angular’s reliance on .data method to store all sort of data such as a class-count map and more
    • if you have large data. you can consider it


    ng style vs ng class

    Implementation detail

    • Angular’s watcher functions for ng-bind and text nodes ({{expression}}) put binding information inside the respective DOM elements using jQuery .data method
    • expensive operation that impacts both load times and time taken to delete nodes
    • this information is not used in actual binding and is mostly for debugging purposes
    • load time reduced by as much as 50%
    
    //from angular 1.3
    config(['$routeProvider', '$compileProvider', function($routeProvider, $compileProvider) {
        $compileProvider.debugInfoEnabled(false);
    }]
    						

    ng-perf.com

    others



    ng-react directive with example

    summary

    summary

    • bind once or one time binding
    • prefer filter in the controller
    • pagination, limitTo, track by
    • $destroy, unbindwatch
    • prefer digest over apply
    • Everything doesnt have to be angular: ng-react

    How to check perfomance

    (if no wifi)

    • pageSpeed

    Free tip

    Look Busy at Work

    1. Ctrl + 1 when big brother is around

    2. Always carry extra bag and print out
    3. do ur personal work, but leave after ur manager

    4. always leave a extra jacket on your chair
    5. compose email during office hours, send it midnight or weekends



    Look busy

    goo.gl/9ylsTB




    www.thatJSdude.com / @mdkhan005



    (youtube.com/user/khanLearning)