10 astuces pour améliorer les performances de son application AngularJS - ngParis - Meetup #11 @ Meetic
1. Make it Better, Do it Faster!
10 astuces pour améliorer les performances
de son application AngularJS
26/05/14
2. Euh… mais t’es qui toi exactement ?
Jonathan Meiss
Manager équipe front-end chez Meetic
@JohnMeiss
3. Mon appli rame quand je veux
afficher une longue liste via ngRepeat
Problème #1
4. <ul>
<li ng-repeat="item in items | startFrom:currentPage*20 | limitTo:20">
{{item.name}}
</li>
</ul>
4
Astuce #1 : limitTo (pagination)
« Réduire le nombre d’éléments à afficher dans une liste
permet de réduire le temps de rendering. »
5. <ul infinite-scroll="addMoreItems()">
<li ng-repeat="item in items">
{{item.name}}
</li>
</ul>
5
Astuce #2 : ngInfiniteScroll
« Charger uniquement les premiers éléments d’une liste
permet de réduire le temps de rendering. »
http://binarymuse.github.io/ngInfiniteScroll
<>
6. <ul>
<li bindonce ng-repeat="item in items" bo-text="item.name"></li>
</ul>
6
Astuce #3 : Bindonce
« Passé les 2000 watchers, la page peut commencer à ramer.
Avec bindonce, cette liste n’a plus qu’un seul watcher ! »
https://github.com/Pasvaz/bindonce
<>
7. <ul>
<li ng-repeat="item in filteredItems()">{{item.name}}</li>
</ul>
7
Astuce #4 : pas de fonction inline !
« Besoin de filtrer la liste dans un contrôleur ?
N’utilisez pas une fonction pour récupérer la liste filtrée ! »
ngRepeat évalue l’expression à chaque $digest,
ce qui est fait très fréquemment.
<ul>
<li ng-repeat="item in items">{{item.name}}</li>
</ul>
8. <li ng-repeat="item in items">
{{item.name | myHeavyFilter}}
</li>
Astuce #5 : utiliser track by (>= 1.2)
Mise à jour d’une liste :
Angular supprime et recrée chaque élément dans le DOM
Mise à jour d’une liste :
Angular fait un diff des deux listes et ne créé / supprime que les éléments non
identiques.
<li ng-repeat="item in items track by item.id">
{{item.name | myHeavyFilter}}
</li>
9. Mon appli rame quand j’utilise ngIf
ou ngShow
Problème #2
10. <div ng-click="toggle = !toggle">Click</div>
<div ng-if="toggle" ng-repeat="item in oldItems">{{item.name}}</div>
<div ng-if="!toggle" ng-repeat="item in newItems">{{item.name}}</div>
10
Astuce #6 : bien utiliser ngShow
« ngShow rend l’élément dans le DOM
même si il n’est pas visible. »
<div ng-click="toggle = !toggle">Click</div>
<div ng-show="toggle" ng-repeat="item in oldItems">{{item.name}}</div>
<div ng-show="!toggle" ng-repeat="item in newItems">{{item.name}}</div>
11. <li ng-repeat="item in items" ng-click="expand = !expand">
{{item.name}}
<span ng-show="expand">{{item.description}}</span>
</li>
11
Astuce #7 : bien utiliser ngIf
« Contrairement à ngShow,
ngIf empêche le rendering de l’élément dans le DOM »
<li ng-repeat="item in items" ng-click="expand = !expand">
{{item.name}}
<span ng-if="expand">{{item.description}}</span>
</li>
13. $resource('/users', {}, {
query: {
method: ’GET’,
cache: true,
isArray: true
}
});
13
Astuce #8 : cache HTTP
« Activer le cache HTTP, permet de mettre en cache la requête
Et ainsi accélérer le prochain affichage de la page »
14. 14
Astuce #9 : $q.all()
« $q.all() permet de paralléliser les promises
puis de traiter chacun des résultats en même temps,
tout en choisissant l’ordre de traitement des réponses. »
16. Ce qui est important,
ce ne sont pas les millisecondes
passées à afficher une page mais
C’est La Performance Utilisateur
17. .when('/accounts', {
templateUrl: 'views/accounts/index.html’,
resolve: {
accountList: ['apiV1', function (apiV1) {
return apiV1.accountList().$promise;
}]
}
});
17
Astuce #10 : resolve
ngRouter attend que toutes les dépendances appelées dans
resolve soient résolues avant d’instancier le contrôleur puis
changer de route.
18. Est-ce que ça veut dire que je
dois utiliser resolve pour
chaque route ?
20. Tout est une question de perception
Montrez à votre utilisateur
qu’une action est en cours…
…ce bon vieux spinner
(HttpInterceptor est votre ami)
21. Tout est une question de perception
Mais ne le mettez pas à
chaque fois non plus !
…l’utilisateur considère une action
comme étant instantanée si elle
prend moins de 100ms
22. Tout est une question de perception
La technique de « l’apéro »…
…donnez-lui quelque chose à grignoter pendant que l’application est en train
de mijoter
Filtrer une liste via des filter et non dans le controller
SANS track by :
- Angular génère un $$hashKey par objet
- Si mise à jour de la liste : nouveau $$hashKey pour chaque élément même si ils sont identiques
Donc tous les éléments sont supprimés et recréés dans le DOM => coûteux surtout si vous appliquez un filtre ou une directive à chaque élément
AVEC track by :
- Angular utilise l’ID de l’objet
- Si mise à jour de la liste : Angular fait un diff de la liste et ne supprime pas / recréé pas les éléments identiques
Autre exemple : un menu qu’on fait apparaître en cliquant sur un bouton.
Utiliser ngShow et pas ngIf, surtout si on rajoute une animation
ngShow compile des directives
ngIf empêche la compilation des directives
Exemple : calculer la somme des âges des membres de Meetic en requêtant chacun des profils
Permet d’éviter l’imbrication de callbacks et donc le synchronisme
On évite ainsi d’afficher une page non complète et que l’utilisateur ait la sensation que l’application rame