Fermer

décembre 7, 2020

Accélérer la division du code d'application angulaire


La vitesse de chargement est essentielle au succès de nos applications. Découvrez comment fonctionne le fractionnement de code et comment il améliore le temps de chargement.

Le fractionnement de code est un fruit facile à utiliser lorsqu'il s'agit d'améliorer la vitesse de chargement de nos applications Web.

 Un chaton blanc et gris et un chaton gris se tiennent très près, donc on dirait qu'ils pourraient se séparer.
(Crédit photo: Maksym Diachenko )

Au lieu d'envoyer un gros paquet avec le JavaScript de l'application entière à l'utilisateur lors de sa visite notre site, nous divisons le bundle en plusieurs bundles plus petits et n'envoyons que le code de la route initiale.

En supprimant le code qui n'est pas nécessaire pour le chemin de rendu critique, nous chargeons notre application (télécharge, rend et devient interactive)

Pourquoi voulons-nous que notre application se charge rapidement?

Expérience utilisateur

Il est universellement reconnu qu'un site réussi doit avoir une bonne expérience utilisateur.

De nombreux aspects contribuent à l'utilisateur d'un site expérience: la charge perfo du site rmance, comment il est facile pour l'utilisateur de trouver ce qu'il recherche, si le site est réactif, facile à utiliser, accessible et attrayant.

Des études montrent que les utilisateurs mobiles apprécient le chargement rapide des pages le plus. Que signifie un chargement de page rapide?

Cela signifie que la page est rendue (les pixels sont peints sur l'écran) rapidement et qu'elle répond aux interactions des utilisateurs (les utilisateurs peuvent cliquer sur les boutons, sélectionner des options).

Quand on visite un site, ça ne fait pas du bien de devoir attendre que le contenu soit affiché. Cela ne fait rien non plus lorsque nous cliquons sur un lien ou un bouton qui ne semble pas répondre.

En fait, attendre est vraiment stressant. Nous devons garder notre calme, prendre de grandes respirations et méditer pour ne pas commencer à cliquer avec rage sur le bouton qui ne répond pas.

La vitesse de chargement initiale de la page est particulièrement critique car les utilisateurs sont susceptibles d'abandonner un site si le contenu prend trop long à afficher ou si la page prend trop de temps pour devenir interactive.

Veuillez noter que les performances de chargement de la page incluent la vitesse de chargement ainsi que la stabilité de la mise en page (mesuré par décalage de disposition cumulatif). Cet article se concentre sur la vitesse de chargement des pages, cependant, je recommande vivement de regarder Optimize for Core Web Vitals par Addy Osmani pour savoir ce qui cause CLS et comment le réduire.

À quelle vitesse une page doit-elle se charger? [19659018] Alors, qu'est-ce qui est considéré comme un temps de chargement de page rapide?

J'adore ce tweet de Monica ingénieur senior chez Google. Elle dit: «Si vous n’établissez pas de contact visuel avec un inconnu pendant le temps qu’il faut à votre application Web pour peindre pour la première fois, elle est trop lente.»

Nous pouvons quantifier davantage la vitesse de chargement initiale grâce aux mesures centrées sur l’utilisateur. fourni par Google Core Web Vitals .

La vitesse de chargement de la page est mesurée en deux ensembles de métriques:

1. Le premier ensemble examine la vitesse de chargement du contenu

First Contentful Paint (FCP) mesure le moment où le premier contenu textuel ou image est affiché à l'écran.

Largest Contentful Paint (LCP) [19659024] mesure le moment où le contenu principal de la page (la plus grande image ou texte) est visible pour les utilisateurs.

LCP est une mesure plus récente utilisée pour estimer quand la page devient utile pour l'utilisateur. Il remplace (Première peinture significative) FMP. Vous pouvez regarder Investigating LCP une conférence amusante et informative de Paul Irish pour en savoir plus.

Le rendu rapide du contenu est extrêmement important car l'utilisateur peut commencer à interagir avec la page . Cela crée une bonne première impression et des performances perçues.

Cependant, ce qui compte encore plus dans une application Web interactive, c'est de pouvoir interagir rapidement avec l'application.

2. Ainsi, le deuxième ensemble de mesures mesure la réactivité de la page

Premier délai d'entrée (FID) Time to Interactive (TTI) et Total Blocking Time (TBT) mesure à quelle vitesse et l'application répond en douceur aux interactions des utilisateurs.

Le tableau ci-dessous résume les délais à viser sur les appareils mobiles et les réseaux 3G moyens. Veuillez consulter web.vitals pour des explications détaillées et des mises à jour.

Métriques Objectif
First Contentful Paint <= 1 s
Largest Contentful Paint < = 2,5 s
Premier délai d'entrée <100 ms
Temps avant interactif <5 s
Temps total de blocage <300 ms
Décalage cumulatif de la disposition <0,1

Pour replacer ces moments dans leur contexte, des études montrent qu'en attendant une réponse aux interactions des utilisateurs:

  • Moins de 200 ms se sentent comme une réaction instantanée.
  • Moins de 1s ressemble toujours au La page fonctionne correctement.
  • Moins de 5 secondes semble faire partie du flux de l'utilisateur.
  • Plus de 8 secondes font perdre leur attention aux utilisateurs et sont susceptibles d'abandonner la tâche.

Quels facteurs affectent le temps de chargement de la page?

Nous avons vu qu'un chargement rapide de la page offre une meilleure expérience utilisateur et que nous pouvons mesurer la vitesse de chargement avec une métrique centrée sur l'utilisateur s. Nous savons viser une plus grande peinture de contenu de moins de 2,5 s et un temps d'interactivité de moins de 5 s.

Cela soulève toujours la question: Quels sont les facteurs qui causent des retards dans le chargement de la page?

Lorsqu'un utilisateur visite notre site, le navigateur fait beaucoup en coulisses pour charger la page et la rendre interactive:

  • Récupérez le document HTML du site
  • Chargez les ressources liées dans le HTML (styles, images, polices web et JS)
  • Effectuez le chemin de rendu critique pour rendre le contenu, et exécutez le JavaScript (qui peut modifier le contenu et les styles et ajouter de l'interactivité à la page)

Voyons ce qui est impliqué dans certaines de ces étapes dans un peu plus de détails afin que nous puissions comprendre comment ils peuvent affecter le temps de chargement de la page.

1. Latence du réseau

Lorsque l'utilisateur entre une URL dans la barre d'adresse du navigateur, encore une fois, le navigateur fait un peu en arrière-plan:

  • Interroge le serveur DNS pour rechercher l'adresse IP du domaine
  • Fait trois -way handshake pour établir une connexion TCP avec le serveur
  • Est-ce que d'autres négociations TLS pour s'assurer que la connexion est sécurisée
  • Envoie une requête HTTP au serveur
  • Attend que le serveur réponde avec le document HTML [19659059] La latence du réseau est le temps écoulé entre le moment où l'utilisateur accède à un site et le moment où le navigateur reçoit le code HTML de la page.

    Bien sûr, le navigateur utilise le cache pour stocker des informations afin que les visites soient plus rapides. Si un technicien de service est enregistré pour un domaine, le navigateur active le technicien de service qui agit alors en tant que proxy réseau et décide s'il faut charger les données du cache ou les demander au serveur.

    Nous pouvons mesurer la latence du réseau par tour Trip Time (RTT) ou Time to First Byte (TTFB).

    La latence du réseau affecte le temps de chargement de la page car le navigateur ne peut pas démarrer le rendu tant qu'il n'a pas le document HTML.

    2. Connectivité réseau

    La connectivité réseau varie énormément. Les réseaux 4G dans différents pays ont des vitesses différentes.

    Même si nous avons maintenant des réseaux 4G et 5G, selon les statistiques un pourcentage important d'utilisateurs sont toujours sur les réseaux 3G et 2G.

    En outre, de nombreux autres facteurs peuvent affecter la vitesse du réseau même si l'utilisateur se trouve sur un réseau rapide.

    Le transfert de fichiers volumineux sur une connexion réseau lente prend beaucoup de temps et retarde la vitesse de chargement de la page.

    Que devons-nous faire? Envoyez moins d'octets sur le réseau et envoyez uniquement ce qui est nécessaire pour la page en cours (et non pour l'application entière).

    3. Différents périphériques utilisateur

    Un autre facteur affectant la vitesse de chargement des pages est la puissance du processeur d'un périphérique.

    Le JavaScript de notre application est exécuté sur le processeur du périphérique de l'utilisateur. L'exécution de JavaScript dans les appareils mobiles médians et bas de gamme avec des processeurs plus lents prend plus de temps que sur les appareils mobiles haut de gamme dotés de processeurs rapides / multicœurs.

    Il est vraiment important pour les performances de notre application que nous n'envoyez pas de code JavaScript non optimisé dont l'exécution prend trop de temps.

    4. Charge de travail du thread principal

    "Le processus de rendu du navigateur est chargé de transformer le code HTML, CSS et JS d'une application Web en pages que nous pouvons voir et avec lesquelles nous pouvons interagir." – Regard intérieur sur un navigateur Web moderne

    C'est le fil conducteur qui fait l'essentiel du travail. Il:

    • Rend le contenu de la page
    • Exécute le JavaScript
    • Répond aux interactions des utilisateurs

    Comme nous pouvons l'imaginer, alors que le thread principal est occupé à faire une tâche, les autres tâches sont retardées. Par exemple, pendant que le thread principal est occupé à exécuter un script, il ne peut pas répondre aux interactions des utilisateurs.

    Il est vraiment important de ne pas lier le thread principal avec JavaScript qui prend trop de temps à exécuter.

    5 . Coût de JavaScript

    Si vous êtes comme moi, vous adorez écrire du code JavaScript. Nous avons besoin de JavaScript pour rendre nos applications interactives et dynamiques.

    Cependant, JavaScript est une ressource coûteuse. Le navigateur doit télécharger, analyser, compiler et exécuter le JavaScipt.

    Dans le passé, l'analyse et la compilation de JavaScript ajoutaient au coût de traitement de JavaScript. Cependant, comme l'explique Addy Osmani dans son article, The Cost of JavaScript in 2019 les navigateurs sont devenus plus rapides pour analyser et compiler JavaScript.

    Maintenant, le coût de JavaScript se compose du temps de téléchargement et d'exécution:

    • Le téléchargement de fichiers JavaScript volumineux prend beaucoup de temps, en particulier sur les connexions réseau lentes.
    • L'exécution de fichiers JavaScript volumineux utilise plus de ressources processeur. Cela affecte particulièrement les utilisateurs d'appareils mobiles médians et bas de gamme.

    Que pouvons-nous faire pour fournir une meilleure vitesse de chargement sur toutes les connexions réseau et tous les appareils?

    La latence du réseau, la connexion réseau et les appareils utilisateur sont tous des facteurs externes qui ne sont pas sous le contrôle d'un développeur frontend. Cependant, ce que nous contrôlons, c'est le JavaScript.

    Voici ce que nous pouvons faire:

    1. Améliorer le temps d'exécution de notre JavaScript

      Chrome DevTools fait référence à un script qui prend plus de 50 millisecondes pour s'exécuter en tant que longue tâche . Les longues tâches retardent le thread principal de répondre aux interactions des utilisateurs, ce qui empêche l'interactivité de la page. Nous pouvons utiliser DevTools pour identifier les tâches longues et optimiser.

    2. Réduisez la taille des bundles JavaScript

      Angular CLI prend déjà en charge les tremblements d'arbres, la minification, l'uglification et le chargement différentiel (moins de JavaScript est expédié pour les navigateurs modernes) pour nous.

      Quoi nous pouvons faire est d'utiliser le fractionnement de code pour diviser le code de notre application en plus petits paquets.

    Voyons maintenant le fractionnement de code plus en détail.

    Division de code

    Le fractionnement de code nous permet de réduire la taille de notre ensemble d'applications (main.js) sans sacrifier les fonctionnalités de notre application. Pour ce faire, il suffit de diviser le bundle JavaScript de l’application en lots plus petits.

    Regroupement d’une application angulaire

    La CLI angulaire utilise webpack comme outil de regroupement. Angular s'occupe de la configuration du webpack pour nous. La configuration permet à webpack de savoir quels bundles sont nécessaires pour amorcer une application Angular.

    Dans une version de production pour une application Angular, webpack crée des bundles runtime.js, polyfills.js et main.js.

    Webpack inclut les modules qui nous importons statiquement (en utilisant l'instruction import en haut de nos modules JS) dans le bundle d'application principal (main.js) . Par défaut, tout le code de l'application est inclus dans main.js .

     Un rectangle appelé main.js contient trois rectangles plus petits: feature-one.module.js, feature-two.module .js, feature-n.module.js

    main.js est une ressource critique, ce qui signifie qu'il modifie le DOM et CSSOM et, par conséquent, affecte le rendu. Pour nous assurer que notre application est chargée rapidement ( LCP <2,5s et TTI <5s ), main.js ne doit inclure que le code nécessaire à la première page de l'application.

    Nous pouvons dire à webpack de diviser le code de l'application en lots séparés en important dynamiquement les modules qui n'ont pas besoin d'être inclus dans le bundle main.js .

    webpack crée des bundles séparés pour les modules qui sont dynamiquement chargé (en utilisant la syntaxe dynamique import () ).

     Sous un rectangle vert appelé main.js se trouvent trois cases grises distinctes intitulées feature-one.module.js, feature-two.module .js et feature-n.module.js

    Le bundle main.js comprend uniquement le code de la page de destination de l'application.

    Remarque: Il est important de ne pas importer statiquement les modules chargés dynamiquement, sinon ils finiront dans le bundle main.js . [19659118] Eager Loading

    Dans le cadre du bundling, webpack ajoute des balises pour les bundles JavaScript nécessaires au bootstrap de notre application Angular dans le document HTML de l'application (index.html) . Ces les bundles sont chargés avec impatience, ce qui signifie que le navigateur téléchargera et traitera ces ressources lorsqu'il recevra le document HTML.

     < head >   
       < script   src  =  " runtime.js "    report >  </  script >   
       < script   src  =  " polyfills.js "    reporter >  </  script >   
       < script [19659125] src  =  " main.js "    reporter >  </  script >   
     </  head >   
     < body >   
       < app-root >  </  app-root > [19659148] </  body > 
    

    Configurer la division de code dans Angular

    L'architecture modulaire utilisée pour construire des applications angulaires se prête bien à la division de code. Nous divisons notre application en fonctionnalités et les fonctionnalités en composants. Les composants sont des blocs de construction autonomes qui contiennent leur HTML, CSS et JavaScript. Leurs dépendances sont injectées, et ils définissent l'interface pour interagir avec d'autres composants. Les modules angulaires sont utilisés pour organiser les composants (et les directives, etc.) dans les fonctionnalités et définir ce qui est partagé avec les autres modules. Nous utilisons le routeur angulaire pour gérer les navigations vers nos pages de fonctionnalités. La division de code peut être effectuée au niveau du composant ou de la route. Dans cet article, nous examinerons le fractionnement de code au niveau de l'itinéraire. La CLI angulaire facilite la configuration du fractionnement de code au niveau de l'itinéraire. Nous utilisons simplement la commande ng pour générer un module spécifiant le nom du module, le chemin de la route et le module parent. Par exemple:
     ng generate module docs --route docs --module app
    
    Et voilà! La CLI angulaire génère le module, un composant et les configurations de route pour nous. La configuration de route est particulièrement intéressante. La CLI ajoute une route dans la configuration de la route pour nous. C'est là que la magie opère 😉.
    
     const  routes :  Routes  =   [  
       {
        chemin :   'docs' 
        loadChildren :   ()   =>   import  ( './ docs / docs.module' )   
                              .  then  ( m  =>  m .  DocsModule )   
      }   
    ] ; 
    
    Comment ça marche?

    Chargement différé

    La configuration de route est un tableau d'objets Route . La propriété loadChildren de l'objet Route indique au routeur que nous voulons charger dynamiquement le bundle de la route au moment de l'exécution. Par défaut, le routeur angulaire charge le bundle lorsque l'utilisateur accède pour la première fois à la route. C'est ce qu'on appelle le chargement asynchrone ou dynamique, ou à la demande ou paresseux. Le fractionnement du code réel est effectué par webpack. La fonction import () indique à webpack de diviser le module demandé et ses enfants dans un ensemble séparé. Pour notre exemple de configuration d'itinéraire ci-dessus, webpack créera un ensemble distinct pour DocsModule nommé quelque chose comme: docs.module.js .

    Avantages de la division de code

    Au lieu d'inclure tout le JavaScript de l'application dans un seul grand ensemble, la division de code nous permet de diviser notre ensemble d'applications en plus petits ensembles. Cela présente de nombreux avantages:
    • L'application se charge plus rapidement ⏱. Le navigateur ne peut pas afficher notre application tant que les ressources critiques n'ont pas été téléchargées. Avec le fractionnement du code, nous pouvons nous assurer que notre bundle d'application initial (main.js) n'a que le code pour la première page. Le résultat est un petit main.js qui est plus rapide à télécharger (qu'un gros paquet contenant tout le code d'application). Ainsi, notre application est rendue plus rapidement et devient interactive plus rapidement, même sur des connexions réseau plus lentes.
    • Plus facile à optimiser pour le temps d'exécution 🏃🏽‍♀️. Il est plus facile d'identifier les bundles qui prennent trop de temps à s'exécuter. Elles sont présentées sous forme de tâches longues dans Chrome DevTools, nous savons donc quel morceau de code analyser et optimiser.
    • Ne gaspille pas les données des utilisateurs 💰. De nombreux utilisateurs ont des forfaits de données mobiles limités. Nous ne voulons pas que nos utilisateurs téléchargent un ensemble volumineux qui utilise leurs données, alors qu'il est fort probable qu'ils ne souhaitent utiliser qu'une partie de l'application. Avec le fractionnement du code, les utilisateurs ne téléchargent JavaScript que pour les pages qu'ils visitent et ne paient donc que pour ce qu'ils utilisent réellement.
    • Meilleur pour la mise en cache. Lorsque nous modifions le code dans un seul bundle, le navigateur invalidera et rechargera uniquement ce bundle 🎁. Les autres bundles qui n'ont pas de mises à jour n'ont pas besoin d'être rechargés, évitant ainsi la demande de réseau et les coûts de latence et de téléchargement associés.

    Et maintenant

    Le fractionnement de code améliore la vitesse de chargement initiale de notre application, mais nous ne le faisons pas. t veux s'arrêter là. Nous devons examiner les stratégies de préchargement pour précharger les lots d'itinéraires afin de nous assurer que les navigations sont également rapides. Utilisez Chrome DevTools et Lighthouse pour mesurer les performances. Si nécessaire, cherchez à intégrer le CSS critique (également appelé CSS au-dessus du pli) de votre application et à reporter la charge des styles non critiques. Regardez dans l'optimisation des images. Utilisez la carte source explorer pour comprendre le contenu de vos bundles JavaScript. Si vous vous interrogez sur le fractionnement de code au niveau des composants dans Angular, je vous recommande de regarder le discours de Brandon Robert sur Revising a Reactive Router with Ivy .

    Conclusion

    Afin d'offrir une bonne expérience utilisateur, il est important que notre application Web rende rapidement et réagisse rapidement aux interactions des utilisateurs. Google Core Web Vitals nous fournit des mesures centrées sur l'utilisateur pour mesurer les performances de charge de notre application. La meilleure pratique consiste à viser une plus grande peinture de contenu de moins de 2,5 secondes et un temps d'interactivité de moins de 5 secondes. Le fractionnement de code est l'une des techniques efficaces qui nous permet de diviser l'ensemble JavaScript de notre application en ensembles plus petits. Le bundle d'application initial ne contient que le JavaScript critique nécessaire pour la page principale, améliorant la vitesse de chargement de notre application. Il est très facile de configurer le fractionnement de code au niveau de l'itinéraire avec la CLI angulaire: exécutez simplement la commande pour générer un chargement différé. module. Webpack divise les modules chargés différés en paquets séparés et Angular s'occupe de la configuration du Webpack pour nous!





Source link