Fermer

février 2, 2022

Amélioration des performances du bundle JavaScript avec le fractionnement de code


Résumé rapide ↬

Dans cet article, Adrian Bece partage plus d'informations sur les avantages et les mises en garde du fractionnement de code et sur la manière dont les performances et les temps de chargement des pages peuvent être améliorés en chargeant dynamiquement des bundles JavaScript coûteux et non critiques.

Les projets construits à l'aide de frameworks basés sur JavaScript embarquent souvent de gros lots de JavaScript qui prennent du temps à télécharger, analyser et exécuter, bloquant le rendu de la page et la saisie de l'utilisateur dans le processus. Ce problème est plus apparent sur les réseaux peu fiables et lents et les appareils bas de gamme. Dans cet article, nous allons couvrir les meilleures pratiques de fractionnement de code et présenter quelques exemples utilisant React. Nous chargeons donc le minimum de JavaScript nécessaire pour rendre une page et chargeons dynamiquement des bundles non critiques de grande taille.

Frameworks basés sur JavaScript comme React a simplifié et efficace le processus de développement d'applications Web, pour le meilleur ou pour le pire. Cette automatisation conduit souvent les développeurs à traiter un framework et à construire des outils comme une boîte noire. C'est une idée fausse courante que le code produit par les outils de construction de framework (Webpack, par exemple) est entièrement optimisé et ne peut plus être amélioré. l'intégralité de l'application Web est contenue dans un seul ou quelques fichiers JavaScriptselon la configuration du projet et les fonctionnalités de l'infrastructure prêtes à l'emploi. Quel problème pourrait-il y avoir si le fichier lui-même est minifié et optimisé ?

Pièges du regroupement

Regardons un exemple simple. Le bundle JavaScript pour notre application Web se compose des six pages suivantes contenues dans des composants individuels. Habituellement, ces composants se composent d'encore plus de sous-composants et d'autres importations, mais nous garderons cela simple pour plus de clarté. et page de profil).

  • Une seule page privée
    Elle est accessible en se connectant (page du tableau de bord).
  • Une page restreinte
    C'est une page d'administration qui a un aperçu de toutes les activités des utilisateurs, des comptes et analytique (page d'administration).
  • Le bundle JavaScript qui se compose d'une page d'accueil, d'une page de connexion, d'une page d'inscription, d'une page de profil, d'une page de tableau de bord et d'une page d'administration.

    ( Grand aperçu)

    Lorsqu'un utilisateur arrive sur une page d'accueil, par exemple, l'ensemble app.min.js avec le code des autres pages est chargé et analysé, ce qui signifie que seule une partie de celui-ci est utilisée et restituée sur la page. Cela semble inefficacen'est-ce pas ? En plus de cela, tous les utilisateurs chargent une partie restreinte de l'application à laquelle seuls quelques utilisateurs pourront avoir accès – la page d'administration. Même si le code est partiellement obscurci dans le cadre du processus de minification, nous risquons d'exposer les points de terminaison de l'API ou d'autres données réservées aux utilisateurs administrateurs. ] la page sur laquelle ils se trouvent actuellement ? En plus de cela, nous devons également nous assurer que les groupes pour les sections restreintes de la page sont chargés uniquement par les utilisateurs autorisés. La réponse réside dans le découpage de code.

    Avant d'entrer dans les détails du découpage de code, rappelons-nous rapidement ce qui rend JavaScript si impactant sur les performances globales.

    Plus après le saut ! Continuez à lire ci-dessous ↓

    Coûts de performance

    L'effet de JavaScript sur les performances consiste en téléchargement, analyse et coûts d'exécution.

    Comme tout fichier référencé et utilisé sur un site internet, il doit d'abord être téléchargé depuis un serveur. La rapidité avec laquelle le fichier est téléchargé dépend de la vitesse de connexion et de la taille du fichier lui-même. Les utilisateurs peuvent naviguer sur Internet à l'aide de réseaux lents et peu fiables. Ainsi, la minification, l'optimisation et le fractionnement du code des fichiers JavaScript garantissent que l'utilisateur télécharge le plus petit fichier possible.

    Temps de chargement estimés pour une application JavaScript improvisée. Le tableau montre la différence de temps de chargement entre les réseaux mobiles (jusqu'à 3 s) et câblés (0,96 s). Les utilisateurs ont une expérience de chargement différente selon le type de réseau.

    Temps de chargement estimés pour une application JavaScript improvisée. Remarquez la différence de temps de chargement entre les réseaux mobiles et câblés. Les utilisateurs ont une expérience de chargement différente selon le type de réseau. ( Grand aperçu )

    Contrairement au fichier image, par exemple, qui n'a besoin d'être rendu qu'une fois le fichier téléchargé, les fichiers JavaScript doivent être parsés, compilés et exécutés. Il s'agit d'une opération gourmande en CPU qui bloque le thread principal rendant la page qui ne répond pas pendant ce temps. Un utilisateur ne peut pas interagir avec la page pendant cette phase même si le contenu peut être affiché et a apparemment fini de se charger. Si le script prend trop de temps à analyser et à s'exécuter, l'utilisateur aura l'impression que le site est cassé et partira. C'est pourquoi Lighthouse et Core Web Vitals spécifient des métriques First Input Delay (FID) et Total Blocking Time (TBT) pour mesurer l'interactivité du site et la réactivité des entrées.

    JavaScript est également un ressource bloquant le renduce qui signifie que si le navigateur rencontre un script dans le document HTML qui n'est pas différé, il n'affiche pas la page tant qu'il n'a pas chargé et exécuté le script. Les attributs HTML async et defer signalent au navigateur de ne pas bloquer le traitement de la page, cependant, le thread CPU est toujours bloqué et le script doit être exécuté avant le la page devient sensible à l'entrée de l'utilisateur.

    Les performances du site Web ne sont pas cohérentes sur tous les appareils. Il existe une large gamme d'appareils disponibles sur le marché avec différentes spécifications de CPU et de mémoire, il n'est donc pas surprenant que la différence de temps d'exécution JavaScript entre les appareils haut de gamme et les appareils moyens soit énorme.

    Le tableau montre. la différence de temps de traitement JavaScript entre les appareils haut de gamme, moyens et bas de gamme.

    Les temps de traitement JavaScript sont très différents entre les appareils haut de gamme, moyens et bas de gamme (Source de l'image : 'Le coût de JavaScript en 2019' par Addy Osmani) ( Grand aperçu )

    Pour répondre à un large éventail de spécifications d'appareils et de types de réseaux, nous ne devrions expédier que le code critique. Pour les applications Web basées sur JavaScript, cela signifie que seul le code utilisé sur cette page particulière doit être chargé, car le chargement simultané de l'ensemble complet de l'application peut entraîner des temps d'exécution plus longs et, pour les utilisateurs, un temps d'attente plus long jusqu'à ce que la page devienne utilisable et réactif à l'entrée. Pour notre exemple, cela signifierait que les pages individuelles devraient être divisées en leurs groupes respectifs – homepage.min.jslogin.min.jsdashboard.min.js etc.

    Lorsque l'utilisateur accède initialement à la page d'accueil, le bundle principal du fournisseur contenant le framework et d'autres dépendances partagées doit être chargé à côté du bundle de la page d'accueil. L'utilisateur clique sur un bouton qui bascule un mode de création de compte. Lorsque l'utilisateur interagit avec les entrées, la coûteuse bibliothèque de vérification de la force du mot de passe est chargée dynamiquement. Lorsqu'un utilisateur crée un compte et se connecte avec succès, il est redirigé vers le tableau de bord, et ce n'est qu'alors que l'ensemble du tableau de bord est chargé. Il est également important de noter que cet utilisateur particulier n'a pas de rôle d'administrateur sur l'application Web, de sorte que le bundle d'administration n'est pas chargé.

    Répartition du code entre les bundles. La page d'accueil fait référence au chargement initial de l'application, le module de création de compte fait référence à l'interaction et le tableau de bord à la navigation.

    ( Grand aperçu )

    Importations dynamiques et fractionnement de code dans React

    Le fractionnement de code est disponible prêt à l'emploi pour l'application Create React et d'autres frameworks qui utilisent Webpack comme Gatsby et Next.js. Si vous avez configuré le projet React manuellement ou si vous utilisez un framework qui n'a pas de fractionnement de code configuré prêt à l'emploi, vous devrez consulter la documentation Webpack ou le documentation de l'outil de construction que vous utilisez.

    Fonctions

    Avant de plonger dans les composants React de fractionnement de code, nous devons également mentionner que nous pouvons également coder des fonctions de fractionnement dans React en important dynamiquement eux. L'importation dynamique est du JavaScript vanille, donc cette approche devrait fonctionner pour tous les frameworks. Cependant, gardez à l'esprit que cette syntaxe n'est pas prise en charge par les anciens navigateurs comme Internet Explorer et Opera Mini.

    import("path/to/myFunction.js").then((myFunction) => {
       /* ... */
    });

    Dans l'exemple suivant, nous avons un article de blog avec une section de commentaires. Nous aimerions encourager nos lecteurs à créer un compte et à laisser des commentaires, nous proposons donc un moyen rapide de créer un compte et de commencer à commenter en affichant le formulaire à côté de la section des commentaires s'ils ne sont pas connectés.

     Un article de blog avec une section de commentaires

    ( Grand aperçu )

    Le formulaire utilise une importante bibliothèque de 800 Ko zxcvbn pour vérifier la force du mot de passe, ce qui pourrait s'avérer problématique pour les performances. C'est donc le bon candidat pour le fractionnement de code. C'est exactement le scénario auquel je faisais face l'année dernière et nous avons réussi à obtenir une amélioration notable des performances en divisant le code de cette bibliothèque en un ensemble distinct et en le chargeant dynamiquement.

    Taille de l'ensemble et temps de téléchargement estimés pour le paquet zxcvbn.[19659014]Taille de l'ensemble et temps de téléchargement estimés pour le package <code>zxcvbn</code>. Cette estimation n'inclut pas les temps d'analyse et d'exécution, ce qui affecte également les performances du site Web. (<a href= Grand aperçu

    )

    Voyons à quoi ressemble le composant Comments.jsx.

    import React, { useState } from "react" ;
    importer zxcvbn depuis "zxcvbn" ; /* Nous importons la lib directement */
    
    export const Commentaires = () => {
      const [password, setPassword] = useState("");
      const [passwordStrength, setPasswordStrength] = useState(0);
    
      const onPasswordChange = (événement) => {
        const { valeur } = événement.cible ;
        const { score } = zxcvbn(valeur)
        setPassword(valeur);
        setPasswordStrength(score);
      } ;
    
      retourner (
        
    {/* ... */} Force du mot de passe : {passwordStrength} {/* ... */}
    ); };

    Nous importons directement la bibliothèque zxcvbn et elle est ainsi incluse dans le bundle principal. Le bundle minifié résultant pour notre minuscule composant de publication de blog est un énorme 442kB gzippé ! La bibliothèque React et cette page d'article de blog atteignent à peine 45kB gzippés, nous avons donc considérablement ralenti le chargement initial de cette page en chargeant instantanément cette bibliothèque de vérification de mot de passe.

    Un article de blog avec une section de commentaires sur le à gauche, et le bundle principal avec la bibliothèque zxcvbn importée qui fait 442 Ko à droite.

    ( Grand aperçu )

    Nous pouvons arriver à la même conclusion en examinant la sortie Webpack Bundle Analyzer pour l'application. Ce rectangle étroit à l'extrême droite est notre composant de publication de blog.

    L'ensemble de l'application est contenu dans un seul ensemble, et la bibliothèque zxcvbn est la plus grande partie de l'ensemble. C'est encore plus grand que la dépendance de react-dom. C'est encore plus grand que la dépendance de réaction-dom. (<a href= Grand aperçu

    )

    La vérification du mot de passe n'est pas essentielle pour le rendu de la page. Sa fonctionnalité n'est requise que lorsque l'utilisateur interagit avec la saisie du mot de passe. Alors, divisons le code zxcvbn dans un bundle séparé, importons-le dynamiquement et chargeons-le uniquement lorsque la valeur d'entrée du mot de passe change, c'est-à-dire lorsque l'utilisateur commence à taper son mot de passe. Nous devons supprimer l'instruction import et ajouter l'instruction d'importation dynamique à la fonction de gestionnaire d'événements de mot de passe onChange.

    import React, { useState } from "react" ;
    
    export const Commentaires = () => {
      /* ... */
      const onPasswordChange = (événement) => {
        const { valeur } = événement.cible ;
        setPassword(valeur);
    
        /* Importation dynamique - renommez l'importation par défaut en nom de bibliothèque pour plus de clarté */
        import("zxcvbn").then(({par défaut : zxcvbn}) => {
          const { score } = zxcvbn(valeur);
          setPasswordStrength(score);
        });
      } ;
    
      /* ... */
    }

    Voyons comment notre application se comporte maintenant après avoir déplacé la bibliothèque vers une importation dynamique.

    Comme nous pouvons le voir sur la vidéo, le chargement initial de la page est d'environ 45kB qui ne couvre que les dépendances du framework et les composants de la page de publication de blog. C'est le cas idéal puisque les utilisateurs pourront obtenir le contenu beaucoup plus rapidement, en particulier ceux qui utilisent des connexions réseau plus lentes.

    Une fois que l'utilisateur commence à saisir le mot de passe, nous pouvons voir le bundle pour le zxcvbn apparaît dans l'onglet réseau et le résultat de l'exécution de la fonction s'affiche sous l'entrée. Même si ce processus se répète à chaque pression sur une touche, le fichier n'est demandé qu'une seule fois et il s'exécute instantanément une fois qu'il est disponible. Sortie de l'analyseur.

    Le plus petit faisceau de couleur bleue à droite et le gros faisceau à gauche.

    C'est beaucoup mieux. Le plus petit groupe de couleur bleue sur la droite est le groupe "critique" qui se charge instantanément, tandis que le grand groupe sur la gauche est un groupe chargé dynamiquement. ( Grand aperçu )

    Composants React tiers

    Les composants React de fractionnement de code sont simples dans la plupart des cas et se composent des quatre étapes suivantes :

    1. utilisez une exportation par défaut pour un composant que nous voulons fractionner en code ;
    2. importer le composant avec React.lazy ;
    3. afficher le composant en tant qu'enfant de React.Suspense ;
    4. fournir une solution de secours composant à React.Suspense.

    Regardons un autre exemple. Cette fois, nous construisons un composant de sélection de date qui a des exigences que l'entrée de date HTML par défaut ne peut pas respecter. Nous avons choisi react-calendar comme bibliothèque que nous allons utiliser.

    Un composant de sélection de date

    ( Grand aperçu)

    Regardons le composant DatePicker. Nous pouvons voir que le composant Calendar du package react-calendar est affiché de manière conditionnelle lorsque l'utilisateur se concentre sur l'élément d'entrée de date.

    import React, { useState } from " réagir";
    importer le calendrier à partir de "react-calendar" ;
    
    exporter const DatePicker = () => {
      const [showModal, setShowModal] = useState(faux);
    
      const handleDateChange = (date) => {
        setShowModal(faux);
      } ;
    
      const handleFocus = () => setShowModal(true);
    
      retourner (
        
    {showModal && }
    ); } ;

    C'est à peu près une manière standard que presque n'importe qui aurait créé cette application. Exécutons Webpack Bundle Analyzer et voyons à quoi ressemblent les bundles.

    Les bundles analysés par Webpack Bundle Analyzer avec l'intégralité de l'application chargée dans un seul bundle JavaScript où react-calendar en prend une part considérable.

    ( Grand aperçu )

    Tout comme dans l'exemple précédent, l'application entière est chargée dans un seul bundle JavaScript et react-calendar en prend une partie considérable. Voyons si nous pouvons le diviser en code.

    La première chose que nous devons remarquer est que la fenêtre contextuelle Calendar est chargée de manière conditionnelle, uniquement lorsque l'état showModal est défini. Cela fait du composant Calendrier un candidat de choix pour le fractionnement de code.

    Ensuite, nous devons vérifier si Calendrier est une exportation par défaut. Dans notre cas, c'est.

    import Calendar from "react-calendar" ; /* Importation standard */

    Changeons le composant DatePicker pour charger paresseusement le composant Calendar.

    import React, { useState, lazy, Suspense } from "react" ;
    
    const Calendar = lazy(() => import("react-calendar")); /* Importation dynamique */
    
    export const DateOfBirth = () => {
      const [showModal, setShowModal] = useState(faux);
    
      const handleDateChange = (date) => {
        setShowModal(faux);
      } ;
    
      const handleFocus = () => setShowModal(true);
    
      retourner (
        
    {showModal && ( )}
    ); };

    Tout d'abord, nous devons supprimer l'instruction import et la remplacer par l'instruction d'importation lazy. Ensuite, nous devons encapsuler le composant chargé paresseux dans un composant Suspense et fournir un retour qui est rendu jusqu'à ce que le composant chargé paresseux soit disponible.

    Il est important de noter. que fallback est un accessoire obligatoire du composant Suspense. Nous pouvons fournir n'importe quel nœud React valide comme solution de secours :

    • null
      Si nous ne voulons rien afficher pendant le processus de chargement.
    • chaîne
      Si nous voulons simplement afficher un texte.
    • Composant React
      Éléments de chargement de squelettepar exemple.

    Exécutons Webpack Bundle Analyzer et confirmons que le react-calendar a été séparé avec succès du code du bundle principal.

     Les bundles analysés par Webpack Bundle Analyzer où le calendrier de réaction a été divisé en code du bundle principal.

    ( Grand aperçu )

    Composants du projet

    Nous ne sommes pas limités aux composants tiers ou aux packages NPM. Nous pouvons diviser en code pratiquement n'importe quel composant de notre projet. Prenons les itinéraires du site Web, par exemple, et fractionnons le code des composants de page individuels en groupes distincts. De cette façon, nous ne chargerons toujours que le bundle principal (partagé) et un bundle de composants nécessaires pour la page sur laquelle nous nous trouvons actuellement. composants qui sont chargés en fonction de l'emplacement actuel (URL).

    import { Navigation } from "./Navigation" ;
    import {Routes, Route} de "react-router-dom" ;
    importer React depuis "react" ;
    
    importer le tableau de bord depuis "./pages/Dashboard" ;
    importer l'Accueil depuis "./pages/Accueil" ;
    importer À propos de "./pages/À propos" ;
    
    fonction App() {
      retourner (
        
          <Route path="/" element={} />
          <Route path="/dashboard" element={} />
          <Route path="/about" element={} />
        
      );
    }
    
    exporter l'application par défaut ; 

    Chacun de ces composants de page a une exportation par défaut et est actuellement importé de manière non paresseuse par défaut pour cet exemple.

     importez React depuis "react" ;
    
    const Accueil = () => {
      return (/* Composant */);
    } ;
    export default Home ;

    Comme nous l'avons déjà conclu, ces composants sont inclus par défaut dans le bundle principal (en fonction du framework et des outils de construction), ce qui signifie que tout est chargé quelle que soit la route sur laquelle l'utilisateur atterrit. Les composants Tableau de bord et À propos sont chargés sur la route de la page d'accueil, etc.

    Refactorisons nos instructions import comme dans l'exemple précédent et utilisons l'importation lazy vers les composants de page fractionnés en code. Nous devons également imbriquer ces composants sous un seul composant Suspense. Si nous devions fournir un élément de secours différent pour ces composants, nous imbriquerions chaque composant sous un composant Suspense distinct. Les composants ont une exportation par défaut, nous n'avons donc pas besoin de les modifier.

    import { Routes, Route } from "react-router-dom" ;
    import React, { lazy, Suspense } from "react" ;
    
    tableau de bord const = paresseux(() => import("./pages/tableau de bord"));
    const Accueil = paresseux(() => import("./pages/Accueil"));
    const À propos = paresseux(() => import("./pages/À propos"));
    
    fonction App() {
      retourner (
        
          
            <Route path="/" element={} />
            <Route path="/dashboard" element={} />
            <Route path="/about" element={} />
          
        
      );
    }
    
    exporter l'application par défaut ; 

    Et c'est tout ! Les composants de la page sont soigneusement divisés en packages séparés et sont chargés à la demande lorsque l'utilisateur navigue entre les pages. Gardez à l'esprit que vous pouvez fournir un composant de secours comme un spinner ou un squelette de chargement pour offrir une meilleure expérience de chargement sur les réseaux plus lents et les appareils moyens à bas de gamme.

    Les composants de la page sont divisés en packages distincts.

    ( Grand aperçu )

    Que devrions-nous diviser en code ?

    Il est crucial de comprendre quelles fonctions et quels composants doivent être divisés en code en lots distincts dès le départ. De cette façon, nous pouvons diviser le code de manière proactive et dès le début du développement et éviter les pièges de regroupement susmentionnés et devoir tout démêler. nous avons couvert. Voici un bon critère de base à suivre lors du choix des candidats potentiels pour le fractionnement de code :

    • composants de page pour les routes (pages individuelles),
    • composants coûteux ou volumineux à chargement conditionnel (modaux, listes déroulantes, menus, etc.),
    • Fonctions et composants tiers coûteux ou volumineux.

    Nous ne devrions pas être trop zélés avec le fractionnement de code. Bien que nous ayons identifié des candidats potentiels pour le fractionnement de code, nous souhaitons charger dynamiquement des bundles qui ont un impact significatif sur les performances ou les temps de chargement. Nous voulons éviter de créer des bundles d'une taille de quelques centaines d'octets ou de quelques kilo-octets. Ces micro-ensembles peuvent en fait nuire à l'expérience utilisateur et aux performances dans certains cas, comme nous le verrons plus loin dans l'article. après la mise en ligne du projet. Le principal inconvénient du fractionnement de code plus tard dans le cycle de développement est que vous devrez gérer des composants et des modifications à plus grande échelle. Si un composant largement utilisé s'avère être un bon candidat pour le fractionnement de code et qu'il est utilisé dans 50 autres composants, la portée de la demande d'extraction et des modifications serait importante et difficile à tester s'il n'existe aucun test automatisé.

    Un exemple. des problèmes de taille de bundle sur les grands projets

    S'ils ne sont pas résolus à temps, les problèmes de taille de bundle deviennent de plus en plus difficiles et risqués à résoudre et à refactoriser sur des projets plus importants comme celui-ci (noms de fichiers et composants omis exprès). ( Grand aperçu )

    Être chargé d'optimiser les performances de l'ensemble de l'application Web peut être un peu écrasant au début. Un bon point de départ consiste à auditer l'application à l'aide de Webpack Bundle Analyzer ou Source Map Explorer et d'identifier les bundles qui doivent être divisés en code et répondre aux critères susmentionnés. Une autre façon d'identifier ces bundles est d'exécuter un test de performance dans un navigateur ou d'utiliser WebPageTestet de vérifier quels bundles bloquent le thread principal du CPU le plus longtemps.

    Après avoir identifié les candidats au fractionnement de code, nous devons vérifier l'étendue des modifications nécessaires pour fractionner en code ce composant du bundle principal. À ce stade, nous devons évaluer si les avantages du fractionnement du code l'emportent sur la portée des modifications requises et sur l'investissement en temps de développement et de test. Ce risque est minime voire nul au début du cycle de développement.

    Enfin, nous devons vérifier que le composant a été correctement divisé en code et que la taille du bundle principal a diminué. Nous devons également créer et tester le composant pour éviter d'introduire des problèmes potentiels.

    Il existe de nombreuses étapes pour fractionner le code d'un seul composant existant. Résumons donc les étapes dans une liste de contrôle rapide :

    1. Auditez le site à l'aide Bundle Analyzer et Browser Performance Profiler, et identifiez les composants et les bundles plus volumineux qui prennent le plus de temps à s'exécuter. , convertissez-le en exportation par défaut.
    2. Si le composant fait partie de barrel exportsupprimez-le du fichier baril.
    3. Refactor import instructions à utiliser instructions paresseuses.
    4. Enveloppez les composants de fractionnement de code dans le composant Suspense et fournissez une solution de secours.
    5. Évaluez le groupe résultant (taille du fichier et gains de performances). Si le groupe ne réduit pas de manière significative la taille du fichier du groupe ou n'améliore pas les performances, annulez le fractionnement du code.
    6. Vérifiez si le projet se construit correctement et s'il fonctionne sans aucun problème.

    Budgets de performances

    Nous pouvons configurer notre créer des outils et des outils d'intégration continue (CI) pour détecter les problèmes de dimensionnement des bundles dès le début du développement en définissant des budgets de performances qui peuvent servir de base de performances ou de limite générale de taille des actifs. Les outils de création comme Webpack, les outils CI et les outils d'audit de performance comme Lighthouse peuvent utiliser les budgets de performance définis et lancer un avertissement si un ensemble ou une ressource dépasse la limite budgétaire. Nous pouvons ensuite exécuter le fractionnement de code pour les bundles qui sont capturés par le moniteur de budget de performances. Il s'agit d'informations particulièrement utiles pour les révisions de demande d'extraction, car nous vérifions comment les fonctionnalités ajoutées affectent la taille globale du bundle. bundlesizes peut être facilement intégré à n'importe quel outil de build ou CI pour suivre les statistiques de taille de bundle sur la base d'une demande d'extraction. (Image de la documentation bundlesizes) ( Grand aperçu )

    Nous pouvons affiner les budgets de performances pour les adapter aux pires scénarios d'utilisateurs possibles et les utiliser comme référence pour l'optimisation des performances. Par exemple, si nous utilisons le scénario d'un utilisateur naviguant sur le site sur une connexion peu fiable et lente sur un téléphone moyen avec un processeur plus lent comme référence, nous pouvons fournir une expérience utilisateur optimale pour un éventail beaucoup plus large d'appareils utilisateur et de types de réseaux.

    Alex Russell a abordé ce sujet en détail dans son article sur les budgets de performances Web dans le monde réel et a découvert que la taille de budget optimale pour ces pires scénarios se situe quelque part entre 130 Ko et 170kB.

    "Les budgets de performance sont un élément essentiel mais sous-estimé du succès du produit et de la santé de l'équipe. La plupart des partenaires avec lesquels nous travaillons ne sont pas conscients de l'environnement d'exploitation réel et font par conséquent des choix technologiques inappropriés. Nous avons défini un budget en time de <= 5 secondes pour le premier chargement Time-to-Interactive et <= 2s pour les chargements suivants. Nous nous limitons à une configuration périphérique + réseau de référence dans le monde réel pour mesurer les progrès. La ligne de base globale par défaut est un appareil Android d'environ 200 USD sur une liaison à 400 Kbit/s avec un temps d'aller-retour ("RTT") de 400 ms. Cela se traduit par un budget d'environ 130 à 170 Ko de ressources de chemin critique, selon la composition – plus vous incluez de JS, plus le bundle doit être petit. (SSR)

    Une mise en garde importante dont nous devons être conscients est que le composant React Suspense est uniquement destiné à une utilisation côté client, ce qui signifie que le rendu côté serveur (SSR) lancera un error s'il essaie de restituer le composant Suspense quel que soit le composant de secours. Ce problème sera traité dans la prochaine React version 18. Cependant, si vous travaillez sur un projet exécuté sur une ancienne version de React, vous devrez résoudre ce problème.

    Une façon de le résoudre consiste à vérifier si le code s'exécute sur le navigateur, ce qui est une solution simple, sinon un peu hacky.

    const isBrowser = typeof window !== "undefined"
    
    retourner (
      <>
        {isBrowser && componentLoadCondition && (
          <Suspense fallback={}>
            
          
        )}
      >
    )

    Cependant, cette solution est loin d'être parfaite. Le contenu ne sera pas rendu côté serveur, ce qui convient parfaitement aux modaux et autres contenus non essentiels. Habituellement, lorsque nous utilisons SSR, c'est pour améliorer les performances et le référencementnous voulons donc que les composants riches en contenu soient rendus en HTML, afin que les robots puissent les analyser pour améliorer le classement des résultats de recherche.

    Jusqu'à la version React. 18 est publié, l'équipe React recommande d'utiliser la bibliothèque Loadable Components pour ce cas précis. Ce plugin étend les composants lazy import et Suspense de React, et ajoute la prise en charge du rendu côté serveurimportations dynamiques avec des propriétés dynamiques, délais d'expiration personnalisés et plus encore. La bibliothèque de composants chargeables est une excellente solution pour les applications React plus grandes et plus complexes, et le fractionnement de code React de base est parfait pour les petites et certaines applications moyennes. les performances et les temps de chargement peuvent être améliorés en chargeant dynamiquement des bundles JavaScript coûteux et non critiques. Comme avantage supplémentaire du fractionnement de code, chaque bundle JavaScript obtient son hachage uniquece qui signifie que lorsque l'application est mise à jour, le navigateur de l'utilisateur ne télécharge que les bundles mis à jour qui ont des hachages différents.

    Cependant, le fractionnement de code peut être facilement abusé et les développeurs peuvent devenir trop zélés et créer trop de micro bundles qui nuisent à la convivialité et aux performances. Le chargement dynamique d'un trop grand nombre de composants plus petits et non pertinents peut rendre l'interface utilisateur insensible et retardée, ce qui nuit à l'expérience utilisateur globale. Overzealous code-splitting can even harm performance in cases where the bundles are served via HTTP 1.1 which lacks multiplexing.

    Use performance budgets, bundle analyzers, performance monitoring tools to identify and evaluate each potential candidate for code splitting. Use code-splitting in a sensible and temperate way, only if it results in a significant bundle size reduction or noticeable performance improvement.

    References

    Smashing Editorial" width="35" height="46" loading="lazy" decoding="async(vf, yk, il)




    Source link