Concernant la description des indicateurs de performances front-end, l'industrie a ses propres opinions, en résumé, cela est lié aux performances du premier écran et à la fluidité de la page. Cette fois, nous optimiserons les performances d'interaction avec la page. la perspective de l’analyse de la maîtrise de la page. [Recommandation de didacticiel associée : "Tutoriel Angular"]
Qu'est-ce que la maîtrise des pages ?
La fluidité des pages est déterminée par la fréquence d'images FPS (Frames Per Second - images transmises par seconde). Généralement, la fréquence de rafraîchissement de l'écran des navigateurs grand public est de 60 Hz (rafraîchie 60 fois par seconde), et la fréquence d'images optimale est de 60 FPS. la fréquence d'images, plus la page est fluide. 60 Hz signifie que l'écran d'affichage sera actualisé toutes les 16,6 ms, ce qui signifie que le rendu de chaque page doit être terminé dans les 16,6 ms, sinon la page perdra des images et se figera.根因在于:浏览器中的JavaScript 执行和页面渲染会相互阻塞
.
Dans les outils de développement de Chrome, nous pouvons exécuter Cmd+Shift+P et saisir show fps pour ouvrir rapidement le panneau fps, comme le montre la figure suivante :
En observant le panneau FPS, nous pouvons facilement surveiller la fluidité de la page en cours.
1 Facteurs affectant les performances de la page
La fluidité de l'interaction de la page dépend de la fluidité de la réponse de la page, et la réponse de la page est essentiellement le processus de rendu des modifications de l'état de la page sur la page.
Le processus de réponse de la page est à peu près le suivant :
En général, la logique de traitement des événements du gestionnaire d'événements ne prend pas trop de temps, de sorte que les facteurs qui affectent les performances d'Angular résident principalement dans异步事件触发
et变更检测
. Généralement, la logique de traitement des événements du gestionnaire d'événements ne consomme pas trop de temps, de sorte que les facteurs qui affectent les performances d'Angular résident principalement dans le déclenchement d'événements asynchrones et la détection de changements.
Pour Angular, le processus de rendu de page est le processus de détection des changements. On peut comprendre que la détection des changements d'Angular doit être terminée dans un délai de 16,6 ms pour éviter la perte et le décalage du cadre de page.
Les performances de la réponse des pages peuvent être optimisées sous les trois aspects suivants.
(1) Pour l'étape de déclenchement d'événements, le déclenchement d'événements asynchrones peut être réduit pour réduire le nombre total de détections de modifications et de re-rendu ;
(2) Pour l'étape logique d'exécution du gestionnaire d'événements, le temps d'exécution peut être réduit en optimisant les processus complexes. logique de code ;
(3) Pour la phase de liaison des données de détection de détection de changement et de mise à jour du DOM, le nombre de calculs de détection de changement et de données de modèle peut être réduit pour réduire le temps de rendu
pour (2) le gestionnaire d'événements, des problèmes spécifiques doivent être analysés ; en détail et ne sera pas discuté.Se concentrer principalement sur (1) (3) ) Optimisation
2 Plan d'optimisation
2.1 Réduire le déclenchement d'événements asynchrones
Angular Dans le mode de détection de changement par défaut, les événements asynchrones déclencheront la détection de changement global, réduisant ainsi le déclenchement d'événements asynchrones. améliorera considérablement les performances d’Angular.
Les événements asynchrones incluent les événements de macro-tâches et les événements de micro-tâches
L'optimisation des événements asynchrones concerne principalement les événements d'écoute de documents. Par exemple, écoutez les clics, mouseup, mousemove... et d'autres événements sur le document.
Scénario d'événement d'écoute :
Renderer2.listen(document,…)
fromEvent(document,…)
document.addEventListener(…)
L'événement d'écoute DOM doit être supprimé lorsqu'il n'est pas nécessaire de le déclencher.
Par exemple : scénarios d'utilisation de la commande de boîte d'invite
: filtrage des colonnes du tableau, clic ailleurs que sur l'icône ou défilement de la page, masquage de la boîte contextuelle du filtre de colonne.
La méthode précédente consistait à surveiller directement l'événement de clic et l'événement de défilement. du document dans la commande pop. De cette façon : Le seul inconvénient est que la boîte de dialogue ne s'affiche pas, mais il y a toujours des événements de surveillance, ce qui est très déraisonnable.
Solution raisonnable : n'écoutez les événements de clic et de défilement que lorsque la boîte de dialogue est affichée, et supprimez les événements d'écoute lorsqu'ils sont masqués.
Pour les événements d'écoute DOM fréquemment déclenchés, vous pouvez utiliser les opérateurs rjx pour optimiser les événements. Voir Opérateurs Rjx pour plus de détails. Marbres RxJS.
2.2 Détection des changements
Qu'est-ce que la détection des changements ?
Pour comprendre la détection des changements, nous pouvons rechercher des réponses à partir des objectifs de la détection des changements. L'objectif de la détection des changements angulaires est de maintenir le modèle (code TypeScript) et le modèle (HTML) synchronisés. Par conséquent, la détection des changements peut être comprise comme : détecter les changements de modèle et mettre à jour le modèle ( DOM ) en même temps .
Quel est le processus de détection des changements ?
En effectuant la détection des modifications dans un ordre descendant dans l'arborescence des composants, c'est-à-dire que la détection des modifications est effectuée d'abord sur le composant parent, puis sur les composants enfants. Vérifiez d'abord les modifications de données du composant parent, puis mettez à jour le modèle de composant parent, lors de la mise à jour du modèle, lorsqu'il rencontre le composant enfant, il mettra à jour la valeur liée au composant enfant, puis entrera dans le composant enfant pour voir si le composant enfant. L'index de la valeur d'entrée @Input a changé.S'il change, marquez le sous-composant comme sale, ce qui nécessite une détection ultérieure des modifications.Après avoir marqué le sous-composant, continuez à mettre à jour le modèle derrière le sous-composant dans le composant parent. ont été mis à jour, apportez des modifications au sous-composant de détection.
2.2.1 Principe de détection des changements angulaires
Dans le mode par défaut de détection des changements, le principe des événements asynchrones déclenchant la détection des changements d'Angular est qu'angular appelle la méthode tick() d'ApplicationRef lors du traitement des événements asynchrones à l'aide de Zone.js pour s'exécuter à partir du composant racine. au sous-composant. Pendant le processus d'initialisation de l'application Angular, une zone (NgZone) est instanciée, puis toute la logique est exécutée dans l'objet _inner de l'objet.
Zone.js implémente les classes suivantes :
Le principe du processus de détection est grosso modo le suivant :
les opérations utilisateur déclenchent des événements asynchrones (par exemple : événements DOM, requêtes d'interface...)
=> La classe ZoneTask gère les événements. La méthode runTask() de zone est appelée dans la fonction EnsureTask(). Dans la méthode runTask, zone passe l'attribut d'instance _zoneDelegate et appelle le hook de ZoneSpec
=> les trois hooks de ZoneSpec (onInvokeTask, onInvoke, onHasTask) passent le checkStable. () fonction Déclencheur zone.onMicrotaskEmpty.emit(null) notification
=> Le composant racine appelle tick() après avoir écouté onMicrotaskEmpty, et appelle detectChanges()
dans la méthode tick pour démarrer la détection à partir du composant racine
=> ··· refreshView()
appelle executeTemplate()
, dans la méthode executeTemplate
Appelez templateFn()
pour mettre à jour la valeur liée au modèle et au sous-composant (这时候会去检测子组件的
输入引用是否改变,如果有改变,会将子组件标记为
Dirty ,也就是该子组件需要变更检测
)
Organigramme détaillé du principe de détection des changements :
Simplifiez le processus :
déclenchez un événement asynchrone
=> ZoneTask gère l'événement
=> ZoneDelegate appelle le hook ZoneSpec pour déclencher la notification onMicrotaskEmpty
=> le composant racine reçoit la notification onMicrotaskEmpty, exécute tick() et commence à détecter et mettre à jour le dom
Comme le montre le code ci-dessus,当微任务为空的时候才会触发变更检测
.
Organigramme simple du principe de détection des changements :
Blog de référence Zone.js sur l'analyse du code source angulaire.
2.2.2 Solution d'optimisation de la détection des changements
1) Utiliser
le principe du mode OnPush : réduire le temps nécessaire à une détection de changement.
La différence entre le mode OnPush et le mode par défaut est que les événements d'écoute DOM, les événements de minuterie et les promesses ne déclencheront pas la détection des modifications. L'état du composant en mode par défaut est toujours CheckAlways, ce qui signifie que le composant doit être testé à chaque cycle de détection.
En mode OnPush : Les situations suivantes déclencheront la détection de changement
S1 et la référence @Input du composant à modifier.
S2. Événements liés au DOM du composant, y compris les événements liés au DOM de ses sous-composants, tels que cliquer, soumettre et appuyer sur la souris. @HostListener()
Remarque :
les événements DOM surveillés via renderer2.listen() ne déclencheront pas la détection des modifications.
La méthode d'écoute native de dom.addEventListener() ne déclenchera pas la détection des modifications
et les événements d'abonnement observables sont également définis. en même temps.
S4. Utilisez les méthodes suivantes pour déclencher manuellement la détection des modifications :
ChangeDetectorRef.detectChanges() : Déclenchez la détection des modifications pour le composant actuel et les sous-composants non OnPush.
ChangeDetectorRef.markForCheck() : marque la vue actuelle et tous ses ancêtres comme sales, et la détection sera déclenchée lors du prochain cycle de détection.
ApplicationRef.tick() : ne déclenchera pas la détection de changement
2)
Principe d'utilisation de NgZone.runOutsideAngular() : Réduisez le nombre de détections de changements
et écrivez la surveillance globale des événements dom dans le rappel de la méthode NgZone.runOutsideAngular(). ne déclenche pas la détection de changement angulaire. Si le composant actuel n'a pas été mis à jour, vous pouvez exécuter le hook detectorChanges() de ChangeDetectorRef dans la fonction de rappel pour déclencher manuellement la détection des modifications du composant actuel.
Exemple : composant d'icône dynamique app-icon-react
2.2.3 Méthode de débogage
1 : vous pouvez utiliser le plug-in Angular DevTools dans la console du navigateur pour afficher un certain événement DOM et l'état de détection d'angular :
Méthode 2 : Vous pouvez saisir directement : ng.profiler.timeChangeDetection() dans la console pour afficher l'heure de détection. Cette méthode peut afficher l'heure de détection globale. Blog de référence Profilage de la détection des changements angulaires
2.3 Optimisation du modèle (HTML)
2.3.1 Réduire le rendu DOM : ngFor plus trackBy
En utilisant l'attribut trackBy de *ngFor, Angular modifie et restitue uniquement les entrées modifiées sans avoir à recharger la liste complète des entrées.
Par exemple : scénario de tri de table. Si trackBy est ajouté à ngFor, seuls les éléments dom de ligne seront déplacés lors du rendu de la table. Si trackBy n'est pas ajouté, les éléments dom de table existants seront d'abord supprimés, puis les éléments dom de ligne seront ajoutés. Évidemment, les performances du déplacement uniquement des éléments dom seront bien meilleures.
2.3.2 N'utilisez pas de fonctions dans les expressions de modèle.
N'utilisez pas d'appels de fonction dans les expressions de modèle angulaires. Vous pouvez utiliser un tube à la place ou une variable après un calcul manuel. Lorsque des fonctions sont utilisées dans des modèles, que la valeur ait changé ou non, la fonction sera exécutée à chaque fois qu'une détection de changement est effectuée, ce qui affectera les performances.
Scénarios d'utilisation des fonctions dans les modèles :
2.3.3 Réduire l'utilisation de ngFor
L'utilisation de ngFor affectera les performances lorsque la quantité de données est importante.
Exemple :
en utilisant ngFor :
Ne pas utiliser ngFor : performances améliorées d'environ 10 fois