Contenidos
Une étude de cas réelle sur l'optimisation des performances de React SPA.
Les performances du site Web ne concernent pas seulement le temps de chargement. Offrir une expérience utilisateur rapide et réactive est essentiel, en particulier pour les applications de bureau de productivité que les gens utilisent quotidiennement. L'équipe d'ingénierie de technologies de recrutement est passé par un projet de refactoring pour améliorer une de vos applications web, AirSHIFT, pour de meilleures performances de saisie utilisateur. C'est comme ça qu'ils ont fait.
Réponse lente, productivité réduite
AirSHIFT est une application Web de bureau qui aide les propriétaires de magasins tels que les restaurants et les cafés à gérer le travail posté des membres de leur personnel. Construite avec React, l'application d'une seule page fournit des fonctionnalités client riches, notamment plusieurs tableaux de grille d'horaires de travail organisés par jour, semaine, mois, etc.
Au fur et à mesure que l'équipe d'ingénieurs de Recruit Technologies ajoutait de nouvelles fonctionnalités à l'application AirSHIFT, elle commençait à recevoir davantage de commentaires sur les performances lentes. Le directeur technique d'AirSHIFT, Yosuke Furukawa, a déclaré :
Dans une étude de recherche d'utilisateurs, nous avons été surpris lorsque l'un des propriétaires de magasin a déclaré qu'il quitterait son siège pour faire du café après avoir cliqué sur un bouton, juste pour perdre du temps à attendre que la table des équipes se charge.
Après avoir mené des recherches, l'équipe d'ingénieurs s'est rendu compte que nombre de leurs utilisateurs essayaient de charger des tables tournantes massives sur des ordinateurs à faible spécification, comme un ordinateur portable Celeron M à 1 GHz d'il y a 10 ans.
Spinner sans fin sur les appareils bas de gamme.
L'application AirSHIFT bloquait le thread principal avec des scripts coûteux, mais l'équipe d'ingénieurs ne réalisait pas à quel point les scripts étaient chers car ils étaient développés et testés sur des ordinateurs riches en spécifications avec des connexions Wi-Fi rapides.
Lors du chargement de la table de décalage, environ 80% du temps de chargement ont été consommés par l'exécution de scripts.
Après avoir profilé ses performances dans Chrome DevTools avec la limitation du processeur et du réseau activée, il est devenu clair qu'une optimisation des performances était nécessaire. AirSHIFT a formé un groupe de travail pour résoudre ce problème. Voici 5 choses sur lesquelles ils se sont concentrés pour rendre leur application plus réactive aux entrées des utilisateurs.
1. Virtualisez les grandes tables
L'affichage du tableau des équipes a nécessité plusieurs étapes coûteuses : construire le DOM virtuel et le restituer à l'écran au prorata du nombre d'employés et de plages horaires. Par exemple, si un restaurant compte 50 membres actifs et souhaite vérifier son horaire de travail mensuel, il s'agirait d'un tableau de 50 (membres) multiplié par 30 (jours), ce qui donnerait 1 500 composants de cellules à afficher. Il s'agit d'une opération très coûteuse, en particulier pour les appareils à faible spécification. En réalité, les choses étaient pires. À partir de l'enquête, ils ont découvert qu'il y avait des magasins gérant 200 membres du personnel, ce qui nécessitait environ 6 000 composants de cellules dans un seul tableau mensuel.
Pour réduire le coût de cette opération, AirSHIFT a virtualisé la table des équipes. L'application ne monte désormais que les composants dans la fenêtre d'affichage et démonte les composants hors écran.
Avant : rendu de toutes les cellules de la table des décalages.
Après : n'affiche que les cellules dans la fenêtre d'affichage.
Dans ce cas, AirSHIFT a utilisé réagir virtualisé car il y avait des exigences autour de l'activation de tableaux de grille bidimensionnels complexes. Ils explorent également des moyens de convertir la mise en œuvre pour utiliser la fenêtre de réaction à la lumière à l'avenir.
Résultats
La virtualisation des tables à elle seule a réduit le temps de script de 6 secondes (dans un environnement accéléré Macbook Pro + Fast 3G 4x plus lent). Il s'agit de l'amélioration des performances la plus marquante du projet de refactoring.
Avant : environ 10 secondes de script après la saisie de l'utilisateur.
Après : 4 secondes de script après la saisie de l'utilisateur.
2. Audit avec API User Timing
Ensuite, l'équipe AirSHIFT a refactorisé les scripts qui s'exécutent sur l'entrée de l'utilisateur. Les tableau des flammes
de Chrome DevTools
vous permet d'analyser ce qui se passe réellement sur le thread principal. Mais l'équipe AirSHIFT a trouvé plus facile d'analyser l'activité des applications en fonction du cycle de vie de React.
React 16 fournit son suivi des performances via le
API de temps utilisateur, que vous pouvez voir depuis le
tranche horaire
depuis Chrome DevTools. AirSHIFT a utilisé la section Times pour trouver une logique inutile qui s'exécute sur les événements du cycle de vie React.
Réagissez aux événements de temps de l'utilisateur.
Résultats
L'équipe AirSHIFT a découvert qu'un
Réconciliation de l'arbre de réaction
cela se passait juste avant chaque route de navigation. Cela signifiait que React mettait inutilement à jour la table tournante avant les navigations. Une mise à jour inutile de l'état de Redux était à l'origine de ce problème. Le réparer a permis d'économiser environ 750 ms de temps de script. AirSHIFT a également réalisé d'autres micro-optimisations qui ont finalement conduit à une réduction totale de 1 seconde du temps de programmation.
3. Chargement paresseux des composants et transfert de la logique coûteuse aux travailleurs Web
AirSHIFT a une application de chat intégrée. De nombreux propriétaires de magasins communiquent avec les membres de leur personnel via le chat tout en consultant le tableau des équipes, ce qui signifie qu'un utilisateur peut taper un message pendant le chargement du tableau. Si le thread principal est occupé par des scripts qui restituent la table, l'entrée de l'utilisateur peut être gênante.
Pour améliorer cette expérience, AirSHIFT utilise désormais React.lazy et Suspense pour afficher des espaces réservés pour le contenu du tableau tout en chargeant paresseux les composants réels.
L'équipe AirSHIFT a également migré une partie de la logique métier coûteuse à l'intérieur des composants chargés paresseusement vers
travailleurs du Web. Cela a résolu le problème de jank d'entrée utilisateur en libérant le thread principal afin qu'il puisse se concentrer sur la réponse à l'entrée utilisateur.
Normalement, les développeurs sont confrontés à la complexité lorsqu'ils utilisent des travailleurs, mais cette fois Comlink fait le gros du travail pour eux. Vous trouverez ci-dessous un pseudocode expliquant comment AirSHIFT a travaillé sur l'une des opérations les plus coûteuses qu'ils aient jamais eues : le calcul des coûts de main-d'œuvre totaux.
Dans App.js, utilisez React.lazy et Suspense pour afficher le contenu de secours lors du chargement
import React , { lazy , Suspense } from 'react'
const Bonjour = paresseux ( ( ) => import ( './Cost' ) )
const Chargement = ( ) => (
< div > Certains contenus de secours à afficher lors du chargement < / div >
)
exporter la fonction par défaut App ( { userInfo } ) {
retour (
< div >
< Repli suspens = { < Chargement / > } >
< Coût / >
< / Suspense >
< / div >
)
}
Dans le composant Coût, utilisez comlink pour exécuter la logique de calcul
importer React depuis 'react' ;
importer { proxy } depuis 'comlink' ;
const WorkerlizedCostCalc = proxy ( new Worker ( './WorkerlizedCostCalc.js' ) ) ;
exporter la fonction par défaut Coût ( { userInfo } ) {
instance const = attendre new WorkerlizedCostCalc ( ) ;
coût const = attendre instance . calc ( infoutilisateur ) ;
retour < p > { coût } < / p > ;
}
Implémenter une logique de calcul qui s'exécute sur le worker et l'exposer avec comlink
importer { exposer } depuis 'comlink'
importer { someCalculationExpensive } de './CostCalc.js'
exposer ( {
calc ( infoutilisateur ) {
return someCalculationCoûteuse ( userInfo ) ;
}
} , soi ) ;
Résultats
Malgré la quantité limitée de logique sur laquelle ils ont travaillé en tant que test, AirSHIFT a déplacé environ 100 ms de son JavaScript du thread principal au thread de travail (simulé avec une limitation 4x du processeur).
AirSHIFT étudie actuellement s'ils peuvent charger paresseusement d'autres composants et décharger plus de logique sur les travailleurs Web afin de réduire davantage le jank.
4. Établissez un budget de performance
Une fois toutes ces optimisations mises en place, il était primordial de s'assurer que l'application continue de fonctionner dans le temps. AirSHIFT utilise maintenant Taille du paquet ne dépassez pas la taille actuelle des fichiers JavaScript et CSS. En plus de définir ces budgets de base, ils ont créé un tableau de bord pour afficher différents centiles de temps de chargement de la table de décalage afin de vérifier si l'application fonctionne même dans des conditions non idéales.
- Le temps d'exécution du script est maintenant mesuré pour chaque événement Redux
- Les données de performance sont collectées sur recherche élastique
- La performance des 10e, 25e, 50e et 75e centiles de chaque événement est affichée avec Kibana
AirSHIFT surveille maintenant l'événement de chargement de la table de décalage pour s'assurer qu'il se termine dans les 3 secondes pour les utilisateurs du 75e centile. Il s'agit d'un budget non appliqué pour l'instant, mais ils envisagent des notifications push via Elasticsearch lorsqu'ils dépassent votre budget.
Le tableau de bord Kibana affichant les données de performances quotidiennes par centiles.
Résultats
Dans le graphique ci-dessus, vous pouvez voir qu'AirSHIFT atteint désormais principalement le budget de 3 secondes pour les utilisateurs du 75e centile et charge également la table des changements en une seconde pour les utilisateurs du 25e centile.En capturant les données de performances RUM à partir de diverses conditions et appareils, AirSHIFT peut désormais vérifiez si une nouvelle version de fonctionnalité affecte réellement les performances de l'application ou non.
5. Hackathons de performance
Bien que tous ces efforts d'optimisation des performances aient été importants et percutants, il n'est pas toujours facile d'amener les équipes commerciales et d'ingénierie à donner la priorité au développement non fonctionnel. Une partie du défi est que certaines de ces optimisations de performances ne peuvent pas être planifiées. Ils nécessitent de l'expérimentation et une mentalité d'essai et d'erreur.
AirSHIFT organise désormais des hackathons de performance internes d'une journée pour permettre aux ingénieurs de se concentrer uniquement sur le travail lié aux performances. Dans ces hackathons, ils suppriment toutes les limitations et respectent la créativité des ingénieurs, ce qui signifie que toute mise en œuvre qui contribue à la vitesse mérite d'être envisagée. Pour accélérer le hackathon, AirSHIFT divise le groupe en petites équipes et chaque équipe s'affronte pour voir qui peut en tirer le meilleur parti Phare amélioration du score de performance. Les équipes deviennent très compétitives ! 🔥
Résultats
L'approche hackathon fonctionne bien pour eux.
- Les goulots d'étranglement des performances peuvent être facilement repérés en essayant plusieurs approches pendant le hackathon et en mesurant chacune avec Lighthouse.
- Après le hackathon, il est assez facile de convaincre l'équipe quelle optimisation privilégier pour la mise en production.
- C'est aussi un moyen efficace de démontrer l'importance de la vitesse. Tous les participants peuvent comprendre la corrélation entre la façon dont vous codez et comment cela se traduit en performance.
Un effet secondaire intéressant a été que de nombreuses autres équipes d'ingénieurs au sein de Recruit se sont intéressées à cette approche pratique et l'équipe AirSHIFT organise désormais plusieurs hackathons de vitesse au sein de l'entreprise.
résumé
Ce n'était certainement pas le voyage le plus facile pour AirSHIFT de travailler sur ces optimisations, mais cela en valait certainement la peine. AirSHIFT charge maintenant la table de décalage en 1,5 seconde sur la médiane, ce qui représente une amélioration de 6 fois par rapport à ses performances d'avant-projet.
Après la publication des optimisations de performances, un utilisateur a déclaré :
Merci beaucoup d'avoir accéléré le chargement de la table de décalage. L'organisation du travail posté est maintenant beaucoup plus efficace.