Site icon Blog ARC Optimizer

Liste de contrôle de la performance frontend 2019 [PDF, Apple Pages, MS Word]


À propos de l'auteur

Vitaly Friedman aime les beaux contenus et n'aime pas céder facilement. Quand il n’écrit pas ou ne parle pas lors d’une conférence, il court probablement…
Plus à propos de Vitaly

Faisons en 2019… vite! Une liste de contrôle annuelle des performances front-end (PDF / Apple Pages / MS Word), avec tout ce que vous devez savoir pour créer des expériences rapides aujourd'hui. Mis à jour depuis 2016.

Les performances Web sont une bête délicate, n’est-ce pas? Comment savons-nous réellement où nous en sommes en termes de performances et quels sont nos goulots d'étranglement en termes de performances exactement ? Est-ce un JavaScript coûteux, une livraison lente des polices sur le Web, des images lourdes ou un rendu lent? Cela vaut-il la peine d’explorer l’agrégation d’arbres, le levage de portées, le fractionnement de codes et tous les modèles de chargement fantaisistes avec Observateur d’intersection, serveur push, conseils aux clients, HTTP / 2, travailleurs de service et – oh mon – avantage? Et, plus important encore, où commençons-nous même à améliorer les performances et comment établissons-nous une culture de la performance à long terme?

À l'époque, la performance était souvent un après-coup . Souvent différé jusqu’à la fin du projet, cela se résumait à une minification, une concaténation, une optimisation des actifs et éventuellement à quelques ajustements précis dans le fichier de configuration du serveur. Rétrospectivement, les choses semblent avoir considérablement changé.

La performance n’est pas simplement un problème technique: elle compte et, lorsqu’elle est intégrée au flux de travail, les décisions de conception doivent être informées de leurs implications en termes de performances. Les performances doivent être mesurées, surveillées et améliorées en permanence et la complexité croissante du Web pose de nouveaux défis qui rendent difficile le suivi des métriques, car les métriques varient considérablement en fonction du périphérique, du navigateur, du protocole , type de réseau et latence (les CDN, les fournisseurs de services Internet, les caches, les proxies, les pare-feu, les équilibreurs de charge et les serveurs jouent tous un rôle dans les performances.)

Si vous créez une vue d'ensemble de tous les éléments à prendre en compte lors de l'amélioration performances - du tout début du processus jusqu'à la publication finale du site Web - à quoi ressemblerait cette liste? Vous trouverez ci-dessous une liste de contrôle de performance initiale pour 2019 (espérons-le objective et objective) - un aperçu actualisé des problèmes à prendre en compte pour garantir des temps de réponse rapides, une interaction fluide entre l'utilisateur et vos sites ne drainent pas la bande passante de l'utilisateur.

Table Of Contents

(Vous pouvez également simplement télécharger la liste de contrôle PDF (166 KB) ou télécharger le fichier éditable de Pages Apple (275 Ko) ou le fichier .docx (151 Ko). Bonne optimisation, tout le monde!)

Préparatifs: planification et métriques [19659012] Les micro-optimisations sont excellentes pour maintenir les performances sur la bonne voie, mais il est essentiel de garder à l'esprit des objectifs clairement définis - objectifs mesurables susceptibles d'influer sur les décisions prises tout au long du processus. Il existe deux modèles différents, et ceux discutés ci-dessous ont une opinion bien arrêtée - assurez-vous simplement de définir vos propres priorités dès le début.
  1. Établissez une culture de la performance
    Dans de nombreuses organisations, les développeurs front-end savent exactement quoi Les problèmes sous-jacents courants sont les modèles de chargement à utiliser pour les résoudre. Cependant, tant qu’il n’y aura pas d’approbation formelle de la culture de la performance, chaque décision deviendra un champ de bataille de ministères, divisant l’organisation en silos. Vous avez besoin d'un engagement des parties prenantes de l'entreprise et pour l'obtenir, vous devez établir une étude de cas sur la mesure dans laquelle les indicateurs de performance et les indicateurs clés de performance ( KPIs ) sont importants pour eux.

    Sans un fort alignement entre les équipes de développement / conception et les équipes commerciales / marketing, la performance ne sera pas durable. Étudiez les réclamations courantes qui entrent en contact avec le service clientèle et voyez comment l'amélioration des performances peut aider à résoudre certains de ces problèmes courants.

    Exécutez des expériences de performance et mesurez les résultats, sur votre mobile comme sur votre ordinateur de bureau. Cela vous aidera à construire une étude de cas personnalisée avec des données réelles. En outre, l'utilisation de données provenant d'études de cas et d'expériences publiées dans WPO Stats contribuera à sensibiliser davantage les entreprises aux raisons pour lesquelles les performances sont importantes et à l'impact qu'elles ont sur l'expérience utilisateur et les statistiques de l'entreprise. Dire que les performances importent à elles seules ne suffit pas cependant, vous devez également établir des objectifs mesurables et pouvant être suivis et les respecter.

    Comment s'y rendre? Dans son exposé sur La performance du bâtiment à long terme Allison McKnight présente une étude de cas détaillée sur la manière dont elle a contribué à l'établissement d'une culture de la performance à Etsy ( slide ).


Le constructeur de budget de performance de Brad Frost et de le calculateur de budget de performance peut vous aider à définir et à visualiser votre budget de performance. ( Grand aperçu )
  1. Objectif: être au moins 20% plus rapide que votre concurrent le plus rapide.
    Selon recherches psychologiques si vous voulez que les utilisateurs sentent que votre site Web est plus rapide que celui de votre concurrent, vous devez être au moins 20% plus rapide. Étudiez vos principaux concurrents, collectez des statistiques sur leurs performances sur les mobiles et les ordinateurs de bureau et définissez des seuils qui vous aideraient à les dépasser. Pour obtenir des résultats et des objectifs précis, commencez par étudier vos analyses afin de déterminer en quoi consistent vos utilisateurs. Vous pouvez ensuite imiter l’expérience du 90e centile pour les tests.

    Pour avoir une bonne première idée de la performance de vos concurrents, vous pouvez utiliser Chrome UX Report ( CrUX un jeu de données RUM prêt à l'emploi, l'introduction vidéo. de Ilya Grigorik), Tableau de bord de la vitesse (fournit également un estimateur d'impact sur les revenus), Comparaison du test d'expérience utilisateur réelle ou SiteSpeed ​​CI (sur la base de tests synthétiques) .

    Note : Si vous utilisez Page Speed ​​Insights (non, il n'est pas obsolète), vous pouvez obtenir les données de performances CrUX pour des pages spécifiques au lieu des seuls agrégats. Ces données peuvent être beaucoup plus utiles pour définir des objectifs de performance pour des actifs tels que «page de destination» ou «liste de produits». Et si vous utilisez CI pour tester les budgets, vous devez vous assurer que votre environnement testé correspond à CrUX si vous utilisez CrUX pour définir la cible (merci Patrick Meenan! ).

    Collectez des données, configurez-les une feuille de calcul économisez 20% et fixez vos objectifs ( budgets de performance ) de cette façon. Vous avez maintenant quelque chose de mesurable à tester. Si vous gardez le budget à l'esprit et essayez d'exporter le script minimal pour obtenir un délai rapide d'interactivité, vous êtes sur une trajectoire raisonnable.

    Vous avez besoin de ressources pour commencer?

    Once vous avez un budget en place, intégrez-les dans votre processus de création avec Webpack Performance Hints and Bundlesize CI Lightouse PWMetrics ou Sitespeed CI pour appliquer les budgets sur les demandes de tirage et fournir un historique des scores dans les commentaires des relations publiques. Si vous avez besoin de quelque chose de personnalisé, vous pouvez utiliser webpagetest-charts-api une API de points de terminaison permettant de créer des graphiques à partir des résultats WebPagetest.

    Par exemple, tout comme Pinterest vous pourriez créez une règle personnalisée eslint qui interdit l'importation à partir de fichiers et de répertoires connus pour être très dépendants et qui alourdiraient le paquet. Configurez une liste de paquets «sûrs» pouvant être partagés par toute l’équipe.

    Au-delà des budgets de performance, pensez aux tâches client critiques les plus bénéfiques pour votre entreprise. Fixez et discutez des seuils de temps acceptables pour les actions critiques et établissez des marques de chronométrage pour l'utilisateur "UX ready" sur lesquelles l'ensemble de l'organisation a convenu. Dans de nombreux cas, les parcours des utilisateurs vont toucher le travail de nombreux départements différents. Par conséquent, un alignement sur les délais acceptables aidera à soutenir ou à empêcher les discussions sur les performances à venir. Assurez-vous que les coûts supplémentaires liés aux ressources et aux fonctionnalités ajoutées sont visibles et bien compris.

    En outre, comme l’a suggéré Patrick Meenan, il est utile de de planifier une séquence de chargement et des compromis au cours du processus de conception. Si vous définissez en priorité les parties les plus critiques et définissez leur ordre d'apparition, vous saurez également ce qui peut être retardé. Idéalement, cette commande reflètera également la séquence de vos importations CSS et JavaScript, ce qui facilitera leur traitement pendant le processus de construction. En outre, considérez ce que l'expérience visuelle devrait être dans les états "entre-deux", pendant le chargement de la page (par exemple, lorsque les polices Web ne sont pas encore chargées).

    Planification, planification, planification. Il serait peut-être tentant de se lancer rapidement dans des optimisations «faciles à mettre en œuvre» - et cela pourrait éventuellement être une bonne stratégie pour des victoires rapides - mais il sera très difficile de maintenir la priorité des performances sans planifier et définir de manière réaliste les performances.

La différence entre la première peinture, la première peinture contenant le contenu, la première peinture significative, le visuel complet et le temps de l’interactivité. Vue agrandie . Crédit: @ denar90
  1. Choisissez les bonnes métriques
    Toutes les métriques ne sont pas d'égale importance . Étudiez les métriques qui importent le plus pour votre application: elles sont généralement liées à la vitesse à laquelle vous pouvez commencer à restituer les pixels les plus importants de votre produit et à la rapidité avec laquelle vous pouvez fournir une réactivité d'entrée pour ces pixels restitués. Cette connaissance vous donnera la meilleure cible d'optimisation pour les efforts en cours.

    D'une manière ou d'une autre, plutôt que de vous concentrer sur le temps de chargement complet de la page (via onLoad et DOMContentLoaded par exemple), donnez la priorité au chargement de page tel qu'il est perçu par vos clients. Cela signifie que vous devez vous concentrer sur un ensemble de métriques légèrement différent. En fait, choisir la bonne métrique est un processus sans vainqueurs évidents.

    Selon les recherches de Tim Kadlec et les notes de Marcos Iglesias dans son exposé les métriques traditionnelles pourraient être regroupées en quelques ensembles. Habituellement, nous avons besoin de tous ces éléments pour obtenir un tableau complet des performances, et dans votre cas particulier, certains d’entre eux pourraient être plus importants que d’autres.

    • Les métriques basées sur les quantités mesurent le nombre de demandes, leur poids et leur valeur. un score de performance. Bon pour déclencher des alarmes et surveiller les changements au fil du temps, pas très bon pour comprendre l'expérience utilisateur.
    • Les métriques jalons utilisent des états dans la durée du processus de chargement, par ex. Heure du premier octet et Heure de l'interactivité . Bon pour décrire l'expérience utilisateur et la surveillance, pas très bon pour savoir ce qui se passe entre les jalons.
    • Les métriques de rendu fournissent une estimation de la rapidité de rendu du contenu (par exemple, Start Render . ] Indice de vitesse ). Bon pour mesurer et modifier les performances de rendu, mais pas pour mesurer quand un contenu important apparaît et qu'il peut être interagi avec ce dernier.
    • Les mesures personnalisées mesurent un événement personnalisé particulier pour l'utilisateur, par exemple. Twitter Time To First Tweet et Pinterest PinnerWaitTime . Bon pour décrire précisément l’utilisateur, mal pour mettre à l’échelle les métriques et comparer avec les concurrents.

    Pour compléter le tableau, nous recherchions généralement des métriques utiles parmi tous ces groupes. Les plus spécifiques et les plus pertinents sont généralement les suivants:

    • Première peinture significative (FMP)
      Indique le moment où le contenu principal apparaît sur la page, donnant un aperçu de la rapidité avec laquelle le serveur génère tout données. Long FMP indique généralement que JavaScript bloque le thread principal, mais peut également être lié à des problèmes d'arrière-plan / serveur.
    • Time to Interactive (TTI)
      Le point auquel la présentation s'est stabilisée est défini par les principaux contrôles Web. visible, et le fil principal est suffisamment disponible pour gérer les entrées de l'utilisateur - essentiellement le repère temporel lorsqu'un utilisateur peut interagir avec l'interface utilisateur. Mesures essentielles pour comprendre combien attendent un utilisateur doit expérimenter pour utiliser le site sans retard
    • Premier délai d’entrée (FID) ou Réactivité en entrée
    • 19659040] Durée entre le moment où un utilisateur interagit avec votre site et le moment où le navigateur est en mesure de répondre à cette interaction. Complète très bien TTI car il décrit la partie manquante de l'image: que se passe-t-il lorsqu'un utilisateur interagit réellement avec le site? Conçu comme une métrique RUM uniquement. Il existe une bibliothèque JavaScript pour mesurer FID dans le navigateur.
    • Index de vitesse
      Mesure la rapidité avec laquelle le contenu de la page est visuellement peuplé. plus le score est bas, mieux c'est. Le score de l’indice de vitesse est calculé en fonction de la vitesse du progrès visuel, mais il s’agit simplement d’une valeur calculée. Il est également sensible à la taille de la fenêtre d'affichage. Vous devez donc définir une gamme de configurations de test correspondant à votre public cible ( merci, Boris ! ).
    • Temps passé sur la CPU
      . ] Une métrique qui indique à quel point le thread principal est occupé par le traitement de la charge utile. Il montre combien de fois et combien de temps le thread principal est bloqué, travaillant sur la peinture, le rendu, les scripts et le chargement. Un temps de calcul élevé est un indicateur clair d’une expérience janky c’est-à-dire lorsque l’utilisateur subit un décalage notable entre son action et une réponse. Avec WebPageTest, vous pouvez sélectionner "Capturer la chronologie des outils de développement" sur l'onglet "Chrome" pour afficher le détail du thread principal lorsqu'il s'exécute sur tout périphérique utilisant WebPageTest.
    • Impact sur le poids de l'annonce
      Si votre site dépend des revenus générés par la publicité, il est utile de suivre le poids du code lié aux annonces. Le script de Paddy Ganti construit deux URL (une normale et une bloquant les publicités), invite à générer une comparaison vidéo via WebPageTest et signale un delta.
    • Métriques de déviation
      Comme noté par Ingénieurs Wikipedia les données sur les écarts dans vos résultats pourraient vous indiquer la fiabilité de vos instruments et le degré d’attention que vous devez porter aux déviations et aux dépassements. Une variance importante est un indicateur des ajustements nécessaires dans la configuration. Il est également utile de comprendre si certaines pages sont plus difficiles à mesurer de manière fiable, par exemple. en raison de scripts tiers provoquant une variation significative. Il peut également être judicieux de suivre la version du navigateur pour comprendre les baisses de performances lors du déploiement d'une nouvelle version du navigateur.
    • Métriques personnalisées
      Les métriques personnalisées sont définies par vos besoins métier et votre expérience client. Vous devez identifier les pixels importants les scripts critiques les éléments nécessaires CSS et les éléments pertinents et mesurer la rapidité avec laquelle ils sont remis à l'utilisateur. . Pour cela, vous pouvez surveiller Hero Rendering Times ou utiliser Performance API marquant des timestaps particuliers pour les événements importants pour votre entreprise. En outre, vous pouvez collecter des métriques personnalisées avec WebPagetest en exécutant du JavaScript arbitraire à la fin d'un test.

    Steve Souders a une explication détaillée de chaque métrique . Il est important de noter que si le temps interactif est mesuré par l'exécution d'audits automatisés dans l'environnement de laboratoire le premier délai d'entrée représente l'expérience utilisateur de avec . utilisateurs réels connaissant un décalage notable. En général, il est probablement judicieux de toujours mesurer et suivre les deux.

    Selon le contexte de votre application, les mesures préférées peuvent différer: par exemple. pour Netflix TV UI, la réactivité des entrées clés, l'utilisation de la mémoire et TTI sont plus critiques, et pour Wikipedia, les premiers / derniers changements visuels et les mesures du temps passé sur l'unité centrale sont plus importants.

    Note : FID et TTI ne tiennent pas compte du comportement de défilement. le défilement peut se faire indépendamment puisqu'il est hors du fil principal, aussi, pour de nombreux sites de consommation de contenu, ces métriques sont peut-être beaucoup moins importantes ( merci, Patrick! ).


Les métriques de performance centrées sur l'utilisateur fournissent un meilleur aperçu. dans l'expérience utilisateur réelle. Le premier délai d'entrée (FID) est une nouvelle mesure qui tente précisément d'atteindre cet objectif. ( Grand aperçu )
  1. Collectez des données sur un périphérique représentant votre public
    Pour collecter des données précises, nous devons choisir soigneusement les périphériques sur lesquels effectuer les tests. C'est une bonne option pour de choisir un Moto G4 un périphérique Samsung de milieu de gamme, un bon périphérique milieu de route comme le Nexus 5X et un périphérique lent comme l'Alcatel 1X, peut-être dans un laboratoire ouvert pour appareils . Pour tester des appareils à régulation thermique plus lente, vous pouvez également vous procurer un Nexus 2, qui coûte environ 100 USD.

    Si vous n’avez pas un appareil à portée de main, émulez l’expérience mobile sur le bureau en testant sur un réseau étranglé (par exemple, 150 ms RTT, 1,5 Mbps en baisse, 0,7 Mbps en hausse) avec un processeur étranglé (ralentissement 5 ×). Passez éventuellement aux réseaux 3G, 4G et Wi-Fi classiques. Pour que l'impact sur les performances soit plus visible, vous pouvez même introduire les mardis 2G ou configurer un réseau 3G étranglé dans votre bureau pour un test plus rapide.

    N'oubliez pas que sur un téléphone portable appareil, vous devriez vous attendre à un ralentissement de 4 × –5 × par rapport aux ordinateurs de bureau. Les appareils mobiles ont des GPU, des processeurs, des mémoires et des caractéristiques de batterie différentes. Bien que les temps de téléchargement soient critiques pour les réseaux bas de gamme, les temps d'analyse sont critiques pour les téléphones dotés de processeurs lents. En fait, les temps d'analyse sur le mobile sont 36% plus élevés que sur le bureau . Donc toujours test sur un appareil moyen - un appareil très représentatif de votre public

  2. Présentation du jour le plus lent de la semaine. Facebook a introduit les 2G mardi pour augmenter la visibilité et la sensibilité des connexions lentes. ( Source de l'image )
      

    Heureusement, il existe de nombreuses options qui vous aident à automatiser la collecte de données et à mesurer les performances de votre site Web dans le temps, en fonction de ces métriques. N'oubliez pas qu'une bonne image de performances couvre un ensemble d'indicateurs de performance, données de laboratoire et données de terrain :

  • Les outils de test synthétique collectent les données de laboratoire dans un environnement reproductible avec appareil et paramètres réseau prédéfinis (par exemple, Lighthouse WebPageTest ) et
  • La surveillance des utilisateurs réels ( RUM ) permet d'évaluer les interactions de l'utilisateur en continu et collecter les données de terrain (par exemple SpeedCurve New Relic - les deux outils fournissent également des tests de synthèse)

Le premier est particulièrement utile pendant développement car il vous aidera à identifier, isoler et résoudre les problèmes de performances lorsque vous travaillez sur le produit. Ce dernier est utile pour la maintenance à long terme car il vous aidera à comprendre les goulots d'étranglement de vos performances lorsqu'ils se produisent en direct - lorsque les utilisateurs accèdent au site.

En exploitant les API RUM intégrées, telles que Synchronisation de la navigation Synchronisation des ressources Synchronisation de la peinture Les tâches longues etc., ainsi que des outils de test de synthèse et le logiciel RUM fournissent une image complète de performances dans votre application. Vous pouvez utiliser PWMetrics Calibre SpeedCurve mPulse et Boomerang . qui sont toutes d'excellentes options pour la surveillance des performances. En outre, avec l’en-tête Server Timing vous pourriez même
surveiller les performances de l’arrière-plan et de l’interface client au même endroit.

Remarque : Il est toujours préférable de choisir des régulateurs de niveau réseau en dehors du navigateur, en tant que, Par exemple, DevTools a des problèmes d'interaction avec HTTP / 2 push, en raison de la façon dont il est implémenté (merci, Yoav, Patrick!). Pour Mac OS, nous pouvons utiliser Network Link Conditioner pour Windows Windows Traffic Shaper pour Linux netem et pour FreeBSD dummynet .


Lighthouse outil d'audit des performances intégré à DevTools. ( Grand aperçu )
  1. Configuration des profils "propres" et "clients" pour les tests.
    Lors de l'exécution de tests dans des outils de surveillance passive, il s'agit d'une stratégie courante pour désactiver les tâches antivirus et de processeur en arrière-plan, supprimer les transferts de bande passante en arrière-plan et effectuer des tests avec Nettoyez le profil de l'utilisateur sans les extensions du navigateur pour éviter des résultats biaisés ( Firefox Chrome ).

    Cependant, il est également judicieux d’étudier les extensions fréquemment utilisées par vos clients et de les tester également avec un profil "client" dédié . En fait, certaines extensions peuvent avoir des conséquences importantes sur les performances de votre application, et si vos utilisateurs les utilisent beaucoup, vous voudrez peut-être en rendre compte dès le départ. Les résultats de profil "propres" sont à eux seuls trop optimistes et peuvent être écrasés dans des scénarios réels.

  2. Partagez la liste de contrôle avec vos collègues.
    Assurez-vous que la liste de contrôle est familière à tous les membres de votre équipe pour éviter les malentendus. ligne. Chaque décision a des implications en termes de performances, et le projet bénéficierait énormément de la communication correcte par les développeurs front-end des valeurs de performances à l'ensemble de l'équipe, de sorte que tout le monde se sente responsable, pas seulement les développeurs front-end. Décisions de la conception de la carte par rapport au budget d'exécution et aux priorités définies dans la liste de contrôle.

Fixer des objectifs réalistes

  1. Temps de réponse de 100 millisecondes, 60 ips.
    Pour qu'une interaction soit fluide, l'interface dispose de 100 ms pour répondre aux besoins de l'utilisateur. contribution. Plus longtemps que cela, et l'utilisateur perçoit l'application comme lente. RAIL, un modèle de performance centré sur l'utilisateur vous propose des objectifs sains: Pour permettre une réponse inférieure à 100 millisecondes, la page doit permettre au contrôle de revenir au thread principal au plus tard toutes les 50 millisecondes. La latence d'entrée estimée nous indique si nous atteignons ce seuil et, idéalement, il devrait être inférieur à 50 ms. Pour les points à haute pression tels que l’animation, il est préférable de ne rien faire d’autre que possible et le minimum absolu nécessaire.

    RAIL un modèle de performance centré sur l'utilisateur.
      

    De plus, chaque image d'animation doit être terminée en moins de 16 millisecondes, ce qui permet d'atteindre 60 images par seconde (1 seconde ÷ 60 = 16,6 millisecondes), de préférence en moins de 10 millisecondes. Étant donné que le navigateur a besoin de temps pour peindre le nouveau cadre à l'écran, l'exécution de votre code doit être terminée avant d'atteindre la marque des 16,6 millisecondes. Nous commençons à avoir des conversations à propos de 120 images par seconde (par exemple, les nouveaux écrans d'iPad fonctionnent à 120 Hz) et Surma a couvert certaines solutions de performance de rendu pour 120 images par seconde mais ce n'est probablement pas une cible que nous examinons pour l'instant. .

    Soyez pessimiste en ce qui concerne les performances, mais soyez optimiste en ce qui concerne la conception des interfaces et utilisez votre temps mort à bon escient . Évidemment, ces objectifs s'appliquent aux performances d'exécution plutôt qu'aux performances de chargement.

  2. Indice de vitesse <1250, TTI <5 ​​sur 3G, budget de taille de fichier critique
    Bien qu'il puisse être très difficile à atteindre, un bon objectif ultime serait Première peinture significative inférieure à 1 seconde et valeur d'index de vitesse inférieure à 1250. Considérant que la référence est un téléphone Android à 200 USD (par exemple, Moto G4) sur un réseau 3G lent, émulé à une vitesse de transfert de 400 ms RTT et 400 kbps, visez Temps d'interactivité pour les moins de 5 ans et pour les visites répétées, visez moins de 2 ans (réalisable uniquement avec un travailleur de service),

    Notez que lorsque vous parlez de métriques d’interactivité, c’est une bonne idée de de faire la distinction entre Premier processeur inactif et Time To Interactive afin d’éviter les malentendus sur la ligne. Le premier est le premier moment après le rendu du contenu principal (où il y a au moins une fenêtre de 5 secondes où la page est réactive). Ce dernier point est le point où on s'attend à ce que la page soit toujours sensible aux entrées ( merci, Philip Walton! ).

    Nous avons deux contraintes majeures qui façonnent efficacement un raisonnable cible pour la livraison rapide du contenu sur le Web. D'une part, nous avons des contraintes de livraison du réseau en raison de TCP Slow Start . Les 14 premiers kilo-octets du code HTML sont> la partie la plus critique de la charge utile - et la seule partie du budget pouvant être fournie lors du premier aller-retour (tout ce que vous obtenez en 1 seconde à 400 ms RTT en raison du temps de réveil de votre mobile).

    D'autre part, nous avons des contraintes matérielles sur la mémoire et le processeur en raison des temps d'analyse de JavaScript (nous en parlerons plus en détail ultérieurement). Pour atteindre les objectifs énoncés dans le premier paragraphe, nous devons examiner le budget critique de la taille de fichier pour JavaScript. Les avis varient sur ce que ce budget devrait être (et cela dépend fortement de la nature de votre projet), mais un budget de 170 Ko JavaScript déjà compressé prendrait jusqu'à 1 s pour être analysé et compilé sur un téléphone moyen. En supposant que 170 Ko s’étende à 3 × cette taille une fois décompressé (0,7 Mo), cela pourrait déjà sonner le glas d’une expérience utilisateur "décente" sur un Moto G4 ou un Nexus 2.

    Bien sûr, vos données pourraient montrer que les clients ne sont pas sur ces appareils, mais peut-être ne font-ils tout simplement pas partie de vos analyses, car votre service leur est inaccessible en raison de performances médiocres. En fait, Alex Russels, de Google, recommande de viser 130-170KB compressés comme limite supérieure raisonnable et dépasser ce budget devrait être une décision éclairée et délibérée. Dans la réalité, la plupart des produits ne sont même pas proches: la taille moyenne d'un paquet est aujourd'hui d'environ 400 Ko ce qui représente une hausse de 35% par rapport à fin 2015. Sur un appareil mobile de la classe moyenne, cela représente 30-35 secondes pour Time-To-Interactive .

    Nous pourrions aussi aller au-delà du budget par paquet. Par exemple, nous pourrions définir des budgets de performance basés sur les activités du fil principal du navigateur, c’est-à-dire le temps de peinture avant le début du rendu, ou pour suivre la trace des processeurs frontaux . Des outils tels que Calibre SpeedCurve et Bundlesize peuvent vous aider à contrôler vos budgets et peuvent être intégrés à votre processus de construction.

    budget de performance ne devrait probablement pas être une valeur fixe. Selon la connexion réseau, les budgets de performances doivent être adaptés à mais la charge utile d'une connexion plus lente est beaucoup plus "coûteuse", quelle que soit la façon dont ils sont utilisés.


De Fast Par défaut: Meilleures pratiques de chargement moderne de Addy Osmani (Diapo 19)
  

Les budgets de performance devraient s'adapter en fonction des conditions de réseau d'un appareil mobile moyen. (Source de l'image: Katie Hempenius ) ( de grande taille )

Définition de l’environnement

  1. Choisissez et configurez vos outils de construction.
    Ne faites pas trop attention à ce qui est soi-disant cool de nos jours . Tenez-vous en à votre environnement pour construire, que ce soit Grunt, Gulp, Webpack, Parcel ou une combinaison d'outils. Tant que vous obtenez les résultats dont vous avez besoin et que vous n’avez aucun problème à maintenir votre processus de génération, vous vous en sortez parfaitement.

    Parmi les outils de compilation, Webpack semble être l'outil le plus établi, avec littéralement des centaines de plug-ins disponibles pour optimiser la taille de vos générations. Bien démarrer avec Webpack peut être difficile. Donc, si vous voulez commencer, il existe d'excellentes ressources:

  2. Utilisez l’amélioration progressive comme valeur par défaut.
    Conservez l’optimisation progressive comme principe directeur de votre architecture frontale et le déploiement est une valeur sûre. Commencez par concevoir et développer l'expérience de base, puis enrichissez l'expérience avec des fonctionnalités avancées pour les navigateurs performants, en créant des expériences résilientes . If your website runs fast on a slow machine with a poor screen in a poor browser on a sub-optimal network, then it will only run faster on a fast machine with a good browser on a decent network.
  3. <! -

    If you need a practical implementation of the strategy on mid-scale and large-scale projects, Scott Jehl’s Modernizing our Progressive Enhancement Delivery article is a good place to start.

    -->

  4. Choose a strong performance baseline.
    With so many unknowns impacting loading — the network, thermal throttling, cache eviction, third-party scripts, parser blocking patterns, disk I/O, IPC latency, installed extensions, antivirus software and firewalls, background CPU tasks, hardware and memory constraints, differences in L2/L3 caching, RTTS — JavaScript has the heaviest cost of the experiencenext to web fonts blocking rendering by default and images often consuming too much memory. With the performance bottlenecks moving away from the server to the clientas developers, we have to consider all of these unknowns in much more detail.

    With a 170KB budget that already contains the critical-path HTML/CSS/JavaScript, router, state management, utilities, framework and the application logic, we have to thoroughly examine network transfer cost, the parse/compile time and the runtime cost of the framework of our choice.

    As noted by Seb Markbåge, a good way to measure start-up costs for frameworks is to first render a view, then delete it and then render again as it can tell you how the framework scales. The first render tends to warm up a bunch of lazily compiled code, which a larger tree can benefit from when it scales. The second render is basically an emulation of how code reuse on a page affects the performance characteristics as the page grows in complexity.

From Fast By Default: Modern Loading Best Practices by Addy Osmani (Slides 18, 19).
  
  1. Evaluate each framework and each dependency.
    Now, not every project needs a framework and not every page of a single-page-application needs to load a framework.. In Netflix’s case, "removing React, several libraries and the corresponding app code from the client-side reduced the total amount of JavaScript by over 200KB, causing an over-50% reduction in Netflix’s Time-to-Interactivity for the logged-out homepage." The team then utilized the time spent by users on the landing page to prefetch React for subsequent pages that users were likely to land on (read on for details).

    It might sound obvious but worth stating: some projects can also benefit benefit from removing an existing framework altogether. Once a framework is chosen, you’ll be staying with it for at least a few years, so if you need to use one, make sure your choice is informed and well considered.

    Inian Parameshwaran has measured performance footprint of top 50 frameworks (against First Contentful Paint — the time from navigation to the time when the browser renders the first bit of content from the DOM). Inian discovered that, out there in the wild, Vue and Preact are the fastest across the board — both on desktop and mobile, followed by React (slides). You could examine your framework candidates and the proposed architecture, and study how most solutions out there perform, e.g. with server-side rendering or client-side rendering, on average.

    Baseline performance cost matters. According to a study by Ankur Sethi"your React application will never load faster than about 1.1 seconds on an average phone in India, no matter how much you optimize it. Your Angular app will always take at least 2.7 seconds to boot up. The users of your Vue app will need to wait at least 1 second before they can start using it." You might not be targeting India as your primary market anyway, but users accessing your site with suboptimal network conditions will have a comparable experience. In exchange, your team gains maintainability and developer efficiency, of course. But this consideration needs to be deliberate.

    You could go as far as evaluating a framework (or any JavaScript library) on Sacha Greif’s 12-point scale scoring system by exploring features, accessibility, stability, performance, package ecosystem, community, learning curve, documentation, tooling, track record, team, compatibility, security for example. But on a tough schedule, it’s a good idea to consider at least the total cost on size + initial parse times before choosing an option; lightweight options such as PreactInfernoVueSvelte or Polymer can get the job done just fine. The size of your baseline will define the constraints for your application’s code.

    A good starting point is to choose a good default stack for your application. Gatsby.js (React), Preact CLIand PWA Starter Kit provide reasonable defaults for fast loading out of the box on average mobile hardware.


(Image credit: Addy Osmani) (Large preview)
  1. Consider using PRPL pattern and app shell architecture.
    Different frameworks will have different effects on performance and will require different strategies of optimization, so you have to clearly understand all of the nuts and bolts of the framework you’ll be relying on. When building a web app, look into the PRPL pattern and application shell architecture. The idea is quite straightforward: Push the minimal code needed to get interactive for the initial route to render quickly, then use service worker for caching and pre-caching resources and then lazy-load routes that you need, asynchronously.
PRPL stands for Pushing critical resource, Rendering initial route, Pre-caching remaining routes and Lazy-loading remaining routes on demand.
  
An application shell is the minimal HTML, CSS, and JavaScript powering a user interface.
  
  1. Have you optimized the performance of your APIs?
    APIs are communication channels for an application to expose data to internal and third-party applications via so-called endpoints. When designing and building an APIwe need a reasonable protocol to enable the communication between the server and third-party requests. Representational State Transfer (REST) is a well-established, logical choice: it defines a set of constraints that developers follow to make content accessible in a performant, reliable and scalable fashion. Web services that conform to the REST constraints, are called RESTful web services.

    As with good ol' HTTP requests, when data is retrieved from an API, any delay in server response will propagate to the end user, hence delaying rendering. When a resource wants to retrieve some data from an API, it will need to request the data from the corresponding endpoint. A component that renders data from several resources, such as an article with comments and author photos in each comment, may need several roundtrips to the server to fetch all the data before it can be rendered. Furthermore, the amount of data returned through REST is often more than what is needed to render that component.

    If many resources require data from an API, the API might become a performance bottleneck. GraphQL provides a performant solution to these issues. Per se, GraphQL is a query language for your API, and a server-side runtime for executing queries by using a type system you define for your data. Unlike REST, GraphQL can retrieve all data in a single request, and the response will be exactly what is required, without over or under-fetching data as it typically happens with REST.

    In addition, because GraphQL is using schema (metadata that tells how the data is structured), it can already organize data into the preferred structure, so, for example, with GraphQL, we could remove JavaScript code used for dealing with state managementproducing a cleaner application code that runs faster on the client.

    If you want to get started with GraphQL, Eric Baer published two fantastic articles on yours truly Smashing Magazine: A GraphQL Primer: Why We Need A New Kind Of API and A GraphQL Primer: The Evolution Of API Design (thanks for the hint, Leonardo!).


A difference between REST and GraphQL, illustrated via a conversation between Redux + REST on the left, an Apollo + GraphQL on the right. (Image source: Hacker Noon) (Large preview)
  1. Will you be using AMP or Instant Articles?
    Depending on the priorities and strategy of your organization, you might want to consider using Google’s AMP or Facebook’s Instant Articles or Apple’s Apple News. You can achieve good performance without them, but AMP does provide a solid performance framework with a free content delivery network (CDN), while Instant Articles will boost your visibility and performance on Facebook.

    The seemingly obvious benefit of these technologies for users is guaranteed performanceso at times they might even prefer AMP-/Apple News/Instant Pages-links over "regular" and potentially bloated pages. For content-heavy websites that are dealing with a lot of third-party content, these options could potentially help speed up render times dramatically.

    Unless they don't. According to Tim Kadlec, for example, "AMP documents tend to be faster than their counterparts, but they don’t necessarily mean a page is performant. AMP is not what makes the biggest difference from a performance perspective."

    A benefit for the website owner is obvious: discoverability of these formats on their respective platforms and increased visibility in search engines. You could build progressive web AMPstoo, by reusing AMPs as a data source for your PWA. Downside? Obviously, a presence in a walled garden places developers in a position to produce and maintain a separate version of their content, and in case of Instant Articles and Apple News without actual URLs (thanks Addy, Jeremy!).

  2. Choose your CDN wisely.
    Depending on how much dynamic data you have, you might be able to "outsource" some part of the content to a static site generatorpushing it to a CDN and serving a static version from it, thus avoiding database requests. You could even choose a static-hosting platform based on a CDN, enriching your pages with interactive components as enhancements (JAMStack). In fact, some of those generators (like Gatsby on top of React) are actually website compilers with many automated optimizations provided out of the box. As compilers add optimizations over time, the compiled output gets smaller and faster over time.

    Notice that CDNs can serve (and offload) dynamic content as well. So, restricting your CDN to static assets is not necessary. Double-check whether your CDN performs compression and conversion (e.g. image optimization in terms of formats, compression and resizing at the edge), support for servers workersedge-side includes, which assemble static and dynamic parts of pages at the CDN’s edge (i.e. the server closest to the user), and other tasks.

    Note: based on research by Patrick Meenan and Andy Davies, HTTP/2 is effectively broken on many CDNsso we shouldn’t be too optimistic about the performance boost there.

Assets Optimizations

  1. Use Brotli or Zopfli for plain text compression.
    In 2015, Google introduced Brotlia new open-source lossless data format, which is now supported in all modern browsers. In practice, Brotli appears to be much more effective than Gzip and Deflate. It might be (very) slow to compress, depending on the settings, but slower compression will ultimately lead to higher compression rates. Still, it decompresses fast. You can also estimate Brotli compression savings for your site.

    Browsers will accept it only if the user is visiting a website over HTTPS though. What’s the catch? Brotli still doesn’t come preinstalled on some servers today, and it’s not straightforward to set up without self-compiling Nginx. Still, it’s not that difficultand its support is coming, e.g. it’s available since Apache 2.4.26. Brotli is widely supported, and many CDNs support it (AkamaiAWSKeyCDNFastlyCloudlareCDN77) and you can enable Brotli even on CDNs that don’t support it yet (with a service worker).

    At the highest level of compression, Brotli is so slow that any potential gains in file size could be nullified by the amount of time it takes for the server to begin sending the response as it waits to dynamically compress the asset. With static compression, however, higher compression settings are preferred.

    Alternatively, you could look into using Zopfli’s compression algorithmwhich encodes data to Deflate, Gzip and Zlib formats. Any regular Gzip-compressed resource would benefit from Zopfli’s improved Deflate encoding because the files will be 3 to 8% smaller than Zlib’s maximum compression. The catch is that files will take around 80 times longer to compress. That’s why it’s a good idea to use Zopfli on resources that don’t change much, files that are designed to be compressed once and downloaded many times.

    If you can bypass the cost of dynamically compressing static assets, it’s worth the effort. Both Brotli and Zopfli can be used for any plaintext payload — HTML, CSS, SVG, JavaScript, and so on.

    The strategy? Pre-compress static assets with Brotli+Gzip at the highest level and compress (dynamic) HTML on the fly with Brotli at level 1–4. Make sure that the server handles content negotiation for Brotli or gzip properly. If you can’t install/maintain Brotli on the server, use Zopfli.

  2. Use responsive images and WebP.
    As far as possible, use responsive images with srcsetsizes and the element. While you’re at it, you could also make use of the WebP format (supported in Chrome, Opera, Firefox 65, Edge 18) by serving WebP images with the element and a JPEG fallback (see Andreas Bovens' code snippet) or by using content negotiation (using Accept headers). Ire Aderinokun has a very detailed tutorial on converting images to WebPtoo.

    Sketch natively supports WebP, and WebP images can be exported from Photoshop using a WebP plugin for Photoshop. Other options are availabletoo. If you’re using WordPress or Joomla, there are extensions to help you easily implement support for WebP, such as Optimus and Cache Enabler for WordPress and Joomla’s own supported extension (via Cody Arsenault).

    It’s important to note that while WebP image file sizes compared to equivalent Guetzli and Zopflithe format doesn’t support progressive rendering like JPEGwhich is why users might see an actual image faster with a good ol' JPEG although WebP images might get faster through the network. With JPEG, we can serve a "decent" user experience with the half or even quarter of the data and load the rest later, rather than have a half-empty image as it is in the case of WebP. Your decision will depend on what you are after: with WebP, you’ll reduce the payload, and with JPEG you’ll improve perceived performance.

    On Smashing Magazine, we use the postfix -opt for image names — for example, brotli-compression-opt.png; whenever an image contains that postfix, everybody on the team knows that the image has already been optimized. And — shameless plug! — Jeremy Wagner even published a Smashing book on WebP.

The Responsive Image Breakpoints Generator automates images and markup generation.
  
  1. Are images properly optimized?
    When you’re working on a landing page on which it’s critical that a particular image loads blazingly fast, make sure that JPEGs are progressive and compressed with mozJPEG (which improves the start rendering time by manipulating scan levels) or GuetzliGoogle’s new open-source encoder focusing on perceptual performance, and utilizing learnings from Zopfli and WebP. The only downside: slow processing times (a minute of CPU per megapixel). For PNG, we can use Pingoand for SVG, we can use SVGO or SVGOMG. And if you need to quickly preview and copy or download all the SVG assets from a website, svg-grabber can do that for you, too.

    Every single image optimization article would state it, but keeping vector assets clean and tight is always worth reminding. Make sure to clean up unused assets, remove unnecessary metadata and reduces the amount of path points in artwork (and thus SVG code). (Thanks, Jeremy!)

    There are more advanced options though. You could:

    • Use Squoosh to compress, resize and manipulate images at the optimal compression levels (lossy or lossless),
    • Use the Responsive Image Breakpoints Generator or a service such as Cloudinary or Imgix to automate image optimization. Also, in many cases, using srcset and sizes alone will reap significant benefits.
    • To check the efficiency of your responsive markup, you can use imaging-heapa command line tool that measure the efficiency across viewport sizes and device pixel ratios.
    • Lazy load images and iframes with lazysizesa library that detects any visibility changes triggered through user interaction (or IntersectionObserver which we’ll explore later).
    • Watch out for images that are loaded by default, but might never be displayed — e.g. in carousels, accordions and image galleries.
    • Consider Swapping Images with the Sizes Attribute by specifying different image display dimensions depending on media queries, e.g. to manipulate sizes to swap sources in a magnifier component.
    • Review image download inconsistencies to prevent unexpected downloads for foreground and background images.
    • To optimize storage interally, you could use Dropbox’s new Lepton format for losslessly compressing JPEGs by an average of 22%.
    • Watch out for the aspect-ratio property in CSS and intrinsicsize attribute which will allow us to set aspect ratios and dimensions for images, so browser can reserve a pre-defined layout slot early to avoid layout jumps during the page load.
    • If you feel adventurous, you could chop and rearrange HTTP/2 streams using Edge workersbasically a real-time filter living on the CDN, to send images faster through the network. Edge workers use JavaScript streams that use chunks which you can control (basically they are JavaScript that runs on the CDN edge that can modify the streaming responses), so you can control the delivery of images. With service worker it’s too late as you can’t control what’s on the wire, but it does work with Edge workers. So you can use them on top of static JPEGs saved progressively for a particular landing page.

    A sample output by imaging-heapa command line tool that measure the efficiency across viewport sizes and device pixel ratios. (Image source) (Large preview)

    The future of responsive images might change dramatically with the adoption of client hints. Client hints are HTTP request header fields, e.g. DPRViewport-WidthWidthSave-DataAccept (to specify image format preferences) and others. They are supposed to inform the server about the specifics of user’s browser, screen, connection etc. As a result, the server can decide how to fill in the layout with appropriately sized images, and serve only these images in desired formats. With client hints, we move the resource selection from HTML markup and into the request-response negotiation between the client and server.

    As Ilya Grigorik notedclient hints complete the picture — they aren’t an alternative to responsive images. "The element provides the necessary art-direction control in the HTML markup. Client hints provide annotations on resulting image requests that enable resource selection automation. Service Worker provides full request and response management capabilities on the client." A service worker could, for example, append new client hints headers values to the request, rewrite the URL and point the image request to a CDN, adapt response based on connectivity and user preferences, etc. It holds true not only for image assets but for pretty much all other requests as well.

    For clients that support client hints, one could measure 42% byte savings on images and 1MB+ fewer bytes for 70th+ percentile. On Smashing Magazine, we could measure 19-32% improvementtoo. Unfortunately, client hints still have to gain some browser support. Under
       consideration in Firefox and
       Edge. However, if you supply both the normal responsive images markup and the tag for Client Hints, then the browser will evaluate the responsive images markup and request the appropriate image source using the Client Hints HTTP headers.

    Not good enough? Well, you can also improve perceived performance for images with the multiple background images technique. Keep in mind that playing with contrast and blurring out unnecessary details (or removing colors) can reduce file size as well. Ah, you need to enlarge a small photo without losing quality? Consider using Letsenhance.io.

    These optimizations so far cover just the basics. Addy Osmani has published a very detailed guide on Essential Image Optimization that goes very deep into details of image compression and color management. For example, you could blur out unnecessary parts of the image (by applying a Gaussian blur filter to them) to reduce the file size, and eventually you might even start removing colors or turn the picture into black and white to reduce the size even further. For background images, exporting photos from Photoshop with 0 to 10% quality can be absolutely acceptable as well. Ah, and don’t use JPEG-XR on the web — "the processing of decoding JPEG-XRs software-side on the CPU nullifies and even outweighs the potentially positive impact of byte size savings, especially in the context of SPAs".

  2. Are videos properly optimized?
    We covered images so far, but we’ve avoided a conversation about good ol' GIFs. Frankly, instead of loading heavy animated GIFs which impact both rendering performance and bandwidth, it’s a good idea to switch either to animated WebP (with GIF being a fallback) or replace them with looping HTML5 videos altogether. Yes, the browser performance is slow with and, unlike with images, browsers do not preload content, but they tend to be lighter and smaller than GIFs. Not an option? Well, at least we can add lossy compression to GIFs with Lossy GIFgifsicle or giflossy.

    Early tests show that inline videos within img tags display 20× faster and decode 7× faster than the GIF equivalent, in addition to being a fraction in file size. Although the support for has landed in Safari Technology Previewwe are far from it being adopted widely as it’s not coming to Blink any time soon.

    Addy Osmani recommends to replace animated GIFs with looping inline videos. The file size difference is noticeable (80% savings). (Large preview)

    In the land of good news though, video formats have been advancing massively over the years. For a long time, we had hoped that WebM would become the format to rule them all, and WebP (which is basically one still image inside of the WebM video container) will become a replacement for dated image formats. But despite WebP and WebM gaining support these days, the breakthrough didn’t happen.

    In 2018, the Alliance of Open Media has released a new promising video format called AV1. AV1 has compression similar to H.265 codec (the evolution of H.264) but unlike the latter, AV1 is free. The H.265 license pricing pushed browser vendors to adopting a comparably performant AV1 instead: AV1 (just like H.265) compress twice as good as WebP.


    AV1 has good chances of becoming the ultimate standard for video on the web. (Image credit: Wikimedia.org) (Large preview)

    In fact, Apple currently uses HEIF format and HEVC (H.265), and all the photos and videos on the latest iOS are saved in these formats, not JPEG. While HEIF and HEVC (H.265) aren’t properly exposed to the web (yet?), AV1 is — and it’s gaining browser support. So adding the AV1 source in your tag is reasonable, as all browser vendors seem to be on board.

    For now, the most widely used and supported encoding is H.264, served by MP4 files, so before serving the file, make sure that your MP4s are processed with a multipass-encodingblurred with the frei0r iirblur effect (if applicable) and moov atom metadata is moved to the head of the file, while your server accepts byte serving. Boris Schapira provides exact instructions for FFmpeg to optimize videos to the maximum. Of course, providing WebM format as an alternative would help, too.

    Video playback performance is a story on its own, and if you’d like to dive into it in details, take a look at Doug Sillar’s series on The Current State of Video and Video Delivery Best Practices that include details on video delivery metrics, video preloading, compression and streaming.

Zach Leatherman’s Comprehensive Guide to Font-Loading Strategies provides a dozen options for better web font delivery.
  1. Are web fonts optimized?
    The first question that’s worth asking if you can get away with using UI system fonts in the first place. If it’s not the case, chances are high that the web fonts you are serving include glyphs and extra features and weights that aren’t being used. You can ask your type foundry to subset web fonts or if you are using open-source fonts, subset them on your own with Glyphhanger or Fontsquirrel. You can even automate your entire workflow with Peter Müller’s subfonta command line tool that statically analyses your page in order to generate the most optimal web font subsets, and then inject them into your page.

    WOFF2 support is great, and you can use WOFF as fallback for browsers that don’t support it — after all, legacy browsers would probably be served well enough with system fonts. There are many, many, many options for web font loading, and you can choose one of the strategies from Zach Leatherman’s "Comprehensive Guide to Font-Loading Strategies," (code snippets also available as Web font loading recipes).

    Probably the better options to consider today are Critical FOFT with preload and "The Compromise" method. Both of them use a two-stage render for delivering web fonts in steps — first a small supersubset required to render the page fast and accurately with the web font, and then load the rest of the family async. The difference is that "The Compromise" technique loads polyfill asynchronously only if font load events are not supported, so you don’t need to load the polyfill by default. Need a quick win? Zach Leatherman has a quick 23-min tutorial and case study to get your fonts in order.

    In general, it’s a good idea to use the preload resource hint to preload fonts, but in your markup include the hints after the link to critical CSS and JavaScript. Otherwise, font loading will cost you in the first render time. Still, it might be a good idea to be selective and choose files that matter most, e.g. the ones that are critical for rendering or that would help you avoiding visible and disruptive text reflows. In general, Zach advises to preload one or two fonts of each family — it also makes sense to delay some font loading if they are less-critical.

    Nobody likes waiting for the content to be displayed. With the font-display CSS descriptorwe can control the font loading behavior and enable content to be readable immediately (font-display: optional) or almost immediately (font-display: swap). However, if you want to avoid text reflowswe still need to use the Font Loading API, specifically to group repaintsor when you are using third party hosts. Unless you can use Google Fonts with Cloudflare Workersof course. Talking about Google Fonts: consider using google-webfonts-helpera hassle-free way to self-host Google Fonts. Always self-host your fonts for maximum control if you can.

    In general, if you use font-display: optionalit might not be a good idea to also use preload as it it’ll trigger that web font request early (causing network congestion if you have other critical path resources that need to be fetched). Use preconnect for faster cross-origin font requests, but be cautious with preload as preloading fonts from a different origin wlll incur network contention. All of these techniques are covered in Zach’s Web font loading recipes.

    Also, it might be a good idea to opt out of web fonts (or at least second stage render) if the user has enabled Reduce Motion in accessibility preferences or has opted in for Data Saver Mode (see Save-Data header). Or when the user happens to have slow connectivity (via Network Information API).

    To measure the web font loading performance, consider the All Text Visible metric (the moment when all fonts have loaded and all content is displayed in web fonts), as well as Web Font Reflow Count after first render. Obviously, the lower both metrics are, the better the performance is. It’s important to notice that variable fonts might require a significant performance consideration. They give designers a much broader design space for typographic choices, but it comes at the cost of a single serial request opposed to a number of individual file requests. That single request might be slow blocking the entire typographic appearance on the page. On the good side though, with a variable font in place, we’ll get exactly one reflow by default, so no JavaScript will be required to group repaints.

    Now, what would make a bulletproof web font loading strategy? Subset fonts and prepare them for the 2-stage-render, declare them with a font-display descriptor, use Font Loading API to group repaints and store fonts in a persistent service worker’s cache. You could fall back to Bram Stein’s Font Face Observer if necessary. And if you’re interested in measuring the performance of font loading, Andreas Marschke explores performance tracking with Font API and UserTiming API.

    Finally, don’t forget to include unicode-range to break down a large font into smaller language-specific fonts, and use Monica Dinculescu’s font-style-matcher to minimize a jarring shift in layout, due to sizing discrepancies between the fallback and the web fonts.

Build Optimizations

  1. Set your priorities straight.
    It’s a good idea to know what you are dealing with first. Run an inventory of all of your assets (JavaScript, images, fonts, third-party scripts and "expensive" modules on the page, such as carousels, complex infographics and multimedia content), and break them down in groups.

    Set up a spreadsheet. Define the basic core experience for legacy browsers (i.e. fully accessible core content), the enhanced experience for capable browsers (i.e. the enriched, full experience) and the extras (assets that aren’t absolutely required and can be lazy-loaded, such as web fonts, unnecessary styles, carousel scripts, video players, social media buttons, large images). A while back, we published an article on "Improving Smashing Magazine’s Performance," which describes this approach in detail.

    When optimizing for performance we need to reflect our priorities. Load the core experience immediately, then enhancementsand then the extras.

  2. Revisit the good ol' "cutting-the-mustard" technique.
    These days we can still use the cutting-the-mustard technique to send the core experience to legacy browsers and an enhanced experience to modern browsers. An updated variant of the technique would use ES2015+ . Modern browsers would interpret the script as a JavaScript module and run it as expected, while legacy browsers wouldn’t recognize the attribute and ignore it because it’s unknown HTML syntax. These days we need to keep in mind that feature detection alone isn’t enough to make an informed decision about the payload to ship to that browser. On its own, cutting-the-mustard deduces device capability from browser version, which is no longer something we can do today. For example, cheap Android phones in developing countries mostly run Chrome and will cut the mustard despite their limited memory and CPU capabilities. Eventually, using the Device Memory Client Hints Headerwe’ll be able to target low-end devices more reliably. At the moment of writing, the header is supported only in Blink (it goes for client hints in general). Since Device Memory also has a JavaScript API which is already available in Chromeone option could be to feature detect based on the API, and fall back to "cutting the mustard" technique only if it’s not supported (thanks, Yoav!).
  3. Parsing JavaScript is expensive, so keep it small.When dealing with single-page applications, we need some time to initialize the app before we can render the page. Your setting will require your custom solution, but you could watch out for modules and techniques to speed up the initial rendering time. For example, here’s how to debug React performance and eliminate common React performance issuesand here’s how to improve performance in Angular. In general, most performance issues come from the initial parsing time to bootstrap the app. JavaScript has a costbut it’s rarely the file size alone that drains on performance. Parsing and executing times vary significantly depending on the hardware of a device. On an average phone (Moto G4), a parsing time alone for 1MB of (uncompressed) JavaScript will be around 1.3–1.4s, with 15–20% of all time on mobile spent on parsing. With compiling in play, just prep work on JavaScript takes 4s on average, with around 11s before First Meaningful Paint on mobile. Reason: parse and execution times can easily be 2–5x times higher on low-end mobile devices. To guarantee high performance, as developers, we need to find ways to write and deploy less JavaScript. That’s why it pays off to examine every single JavaScript dependency in detail. There are many tools to help you make an informed decision about the impact of your dependencies and viable alternatives: An interesting way of avoiding parsing costs is to use binary templates that Ember has introduced in 2017. With them, Ember replaces JavaScript parsing with JSON parsing, which is presumably faster. (Thanks, Leonardo, Yoav!) Measure JavaScript parse and compile times. We can use synthetic testing tools and browser traces to track parse times, and browser implementors are talking about exposing RUM-based processing times in the future. Alternatively, consider using Etsy’s DeviceTiminga little tool allowing you to instruct your JavaScript to measure parse and execution time on any device or browser. Bottom line: while size matters, it isn’t everything. Parse and compiling times don’t necessarily increase linearly when the script size increases.
  4. Are you using tree-shaking, scope hoisting and code-splitting?Tree-shaking is a way to clean up your build process by only including code that is actually used in production and eliminate unused imports in Webpack. With Webpack and Rollup, we also have scope hoisting that allows both tools to detect where import chaining can be flattened and converted into one inlined function without compromising the code. With Webpack, we can also use JSON Tree Shaking as well. Also, you might want to consider learning how to write efficient CSS selectors as well as how to avoid bloat and expensive styles. Feeling like going beyond that? You can also use Webpack to shorten the class names and use scope isolation to rename CSS class names dynamically at the compilation time. Code-splitting is another Webpack feature that splits your code base into "chunks" that are loaded on demand. Not all of the JavaScript has to be downloaded, parsed and compiled right away. Once you define split points in your code, Webpack can take care of the dependencies and outputted files. It enables you to keep the initial download small and to request code on demand when requested by the application. Alexander Kondrov has a fantastic introduction to code-splitting with Webpack and React. Consider using preload-webpack-plugin that takes routes you code-split and then prompts browser to preload them using or . Webpack inline directives also give some control over preload/prefetch Where to define split points? By tracking which chunks of CSS/JavaScript are used, and which aren’t used. Umar Hansa explains how you can use Code Coverage from Devtools to achieve it. If you aren’t using Webpack, note that Rollup shows significantly better results than Browserify exports. While we’re at it, you might want to check out rollup-plugin-closure-compiler and Rollupifywhich converts ECMAScript 2015 modules into one big CommonJS module — because small modules can have a surprisingly high performance cost depending on your choice of bundler and module system.
  5. Can you offload JavaScript into a Web Worker?To reduce the negative impact to Time-to-Interactive, it might be a good idea to look into offloading heavy JavaScript into a Web Worker or caching via a Service Worker. As the code base keeps growing, the UI performance bottlenecks will show up, slowing down the user’s experience. That’s because DOM operations are running alongside your JavaScript on the main thread. With web workerswe can move these expensive operations to a background process that’s running on a different thread. Typical use cases for web workers are prefetching data and Progressive Web Apps to load and store some data in advance so that you can use it later when needed. And you could use Comlink to streamline the communication between the main page and the worker. Still some work to do, but we are getting there. Workerize allows you to move a module into a Web Worker, automatically reflecting exported functions as asynchronous proxies. And if you’re using Webpack, you could use workerize-loader. Alternatively, you could use worker-plugin as well. Note that Web Workers don’t have access to the DOM because the DOM is not "thread-safe", and the code that they execute needs to be contained in a separate file.
  6. Can you offload JavaScript into WebAssembly?We could potentialy also convert JavaScript into WebAssemblya binary instruction format, designed as a portable target for compilation of high-level languages like C/C++/Rust. Its browser support is remarkableand it has recently become viable as function calls between JavaSript and WASM are getting fasterat least in Firefox. In real-world scenarios, JavaScript seems to perform better than WebAssembly on smaller array sizes and WebAssembly performs better than JavaScript on larger array sizes. For most web apps, JavaScript is a better fit, and WebAssembly is best used for computationally intensive web apps, such as web games. However, it might be worth investigating if a switch to WebAssembly would result in noticeable performance improvements. If you’d like to learn more about WebAssembly:
Milica Mihajlija provides a general overview of how WebAssembly works and why it’s useful. (Large preview)
  1. Are you using an ahead-of-time compiler?Use an ahead-of-time compiler to offload some of the client-side rendering to the server and, hence, output usable results quickly. Finally, consider using Optimize.js for faster initial loading by wrapping eagerly invoked functions (it might not be necessary any longer, though).
From Fast By Default: Modern Loading Best Practices by the one-and-only Addy Osmani. Slide 76.
  1. Serve legacy code only to legacy browsers.With ES2015 being remarkably well supported in modern browserswe can use babel-preset-env to only transpile ES2015+ features unsupported by the modern browsers you are targeting. Then set up two buildsone in ES6 and one in ES5. As mentioned above, JavaScript modules are now supported in all major browsersso use use script type="module" to let browsers with ES module support load the file, while older browsers could load legacy builds with script nomodule. And we can automate the entire process with Webpack ESNext Boilerplate. Note that these days we can write module-based JavaScript that runs natively in the browser, without transpilers or bundlers. header provides a way to initiate early (and high-priority) loading of module scripts. Basically, it’s a nifty way to help in maximizing bandwidth usage, by telling the browser about what it needs to fetch so that it’s not stuck with anything to do during those long roundtrips. Also, Jake Archibald has published a detailed article with gotchas and things t keep in mind with ES Modules that’s worth reading. For lodash, use babel-plugin-lodash that will load only modules that you are using in your source. Your dependencies might also depend on other versions of Lodash, so transform generic lodash requires to cherry-picked ones to avoid code duplication. This might save you quite a bit of JavaScript payload. Shubham Kanodia has written a detailed low-maintenance guide on smart bundling: to shipping legacy code to only legacy browsers in production with the code snippet you could use right away.
Jake Archibald has published a detailed article with gotchas and things to keep in mind with ES Modulese.g. inline scripts are deferred until blocking external scripts and inline scripts are executed. (Large preview)
  1. Are you using differential serving for JavaScript?We want to send just the necessary JavaScript through the network, yet it means being slightly more focused and granular about the delivery of those assets. A while back Philip Walton introduced the idea of differential serving. The idea is to compile and serve two separate JavaScript bundles: the “regular” build, the one with Babel-transforms and polyfills and serve them only to legacy browsers that actually need them, and another bundle (same functionality) that has no transforms or polyfills. As a result, we help reduce blocking of the main thread by reducing the amount of scripts the browser needs to process. Jeremy Wagner has published a comprehensive article on differential serving and how to set it up in your build pipeline in 2019, from setting up Babel, to what tweaks you’ll need to make in Webpack, as well as the benefits of doing all this work.
  2. Identify and rewrite legacy code with incremental decoupling.Long-living projects have a tendency to gather dust and dated code. Revisit your dependencies and assess how much time would be required to refactor or rewrite legacy code that has been causing trouble lately. Of course, it’s always a big undertaking, but once you know the impact of the legacy code, you could start with incremental decoupling. First, set up metrics that tracks if the ratio of legacy code calls is staying constant or going down, not up. Publicly discourage the team from using the library and make sure that your CI alerts developers if it’s used in pull requests. polyfills could help transition from legacy code to rewritten codebase that uses standard browser features.
  3. Identify and remove unused CSS/JS.CSS and JavaScript code coverage in Chrome allows you to learn which code has been executed/applied and which hasn't. You can start recording the coverage, perform actions on a page, and then explore the code coverage results. Once you’ve detected unused code, find those modules and lazy load with import() (see the entire thread). Then repeat the coverage profile and validate that it’s now shipping less code on initial load. You can use Puppeteer to programmatically collect code coverage and Canary already allows you to export code coverage resultstoo. As Andy Davies noted, you might want to collect code coverage for both modern and legacy browsers though. There are many other use-cases for Puppeteersuch as, for example, automatic visual diffing or monitoring unused CSS with every build. Furthermore, purgecssUnCSS and Helium can help you remove unused styles from CSS. And if you aren’t certain if a suspicious piece of code is used somewhere, you can follow Harry Roberts' advice: create a 1×1px transparent GIF for a particular class and drop it into a dead/ directory, e.g. /assets/img/dead/comments.gif. After that, you set that specific image as a background on the corresponding selector in your CSS, sit back and wait for a few months if the file is going to appear in your logs. If there are no entries, nobody had that legacy component rendered on their screen: you can probably go ahead and delete it all. For the I-feel-adventurous-department, you could even automate gathering on unused CSS through a set of pages by monitoring DevTools using DevTools.
  4. Trim the size of your JavaScript bundles.As Addy Osmani notedthere’s a high chance you’re shipping full JavaScript libraries when you only need a fraction, along with dated polyfills for browsers that don’t need them, or just duplicate code. To avoid the overhead, consider using webpack-libs-optimizations that removes unused methods and polyfills during the build process. Add bundle auditing into your regular workflow as well. There might be some lightweight alternatives to heavy libraries you’ve added years ago, e.g. Moment.js could be replaced with date-fns or Luxon. Benedikt Rötsch’s research showed that a switch from Moment.js to date-fns could shave around 300ms for First paint on 3G and a low-end mobile phone. That’s where tools like Bundlephobia could help find the cost of adding a npm package to your bundle. You can even integrate these costs with a Lighthouse Custom Audit. This goes for frameworks, too. By removing or trimming the Vue MDC Adapter (Material Components for Vue), styles drop from 194KB to 10KB. Feeling adventurous? You could look into Prepack. It compiles JavaScript to equivalent JavaScript code, but unlike Babel or Uglify, it lets you write normal JavaScript code, and outputs equivalent JavaScript code that runs faster. Alternatively to shipping the entire framework, you could even trim your framework and compile it into a raw JavaScript bundle that does not require additional code. Svelte does itand so does Rawact Babel plugin which transpiles React.js components to native DOM operations at build-time. Pourquoi? Well, as maintainers explain, "react-dom includes code for every possible component/HTMLElement that can be rendered, including code for incremental rendering, scheduling, event handling, etc. But there are applications which do not need all these features (at initial page load). For such applications, it might make sense to use native DOM operations to build the interactive user interface."
In his articleBenedikt Rötsch’s showed that a switch from Moment.js to date-fns could shave around 300ms for First paint on 3G and a low-end mobile phone. (Large preview)
  1. Are you using predictive prefetching for JavaScript chunks?We could use heuristics to decide when to preload JavaScript chunks. Guess.js is a set of tools and libraries that use Google Analytics data to determine which page a user is mostly likely to visit next from a given page. Based on user navigation patterns collected from Google Analytics or other sources, Guess.js builds a machine-learning model to predict and prefetch JavaScript that will be required in each subsequent page. Hence, every interactive element is receiving a probability score for engagement, and based on that score, a client-side script decides to prefetch a resource ahead of time. You can integrate the technique to your Next.js applicationAngular and Reactand there is a Webpack plugin which automates the setup process as well. Obviously, you might be prompting the browser to consume unneeded data and prefetch undesirable pages, so it’s a good idea to be quite conservative in the number of prefetched requests. A good use case would be prefetching validation scripts required in the checkout, or speculative prefetch when a critical call-to-action comes into the viewport. Need something less sophisticated? Quicklink is a small library that automatically prefetches links in the viewport during idle time in attempt to make next-page navigations load faster. However, it’s also data-considerate, so it doesn’t prefetch on 2G or if Data-Saver is on.
  2. Take advantage of optimizations for your target JavaScript engine.Study what JavaScript engines dominate in your user base, then explore ways of optimizing for them. For example, when optimizing for V8 which is used in Blink-browsers, Node.js runtime and Electron, make use of script streaming for monolithic scripts. It allows async or defer scripts to be parsed on a separate background thread once downloading begins, hence in some cases improving page loading times by up to 10%. Practically, use in the so that the browsers can discover the resource early and then parse it on the background thread. Caveat: Opera Mini doesn’t support script defermentso if you are developing for India or Africa, defer will be ignored, resulting in blocking rendering until the script has been evaluated (thanks Jeremy!).
Progressive booting means using server-side rendering to get a quick first meaningful paint, but also include some minimal JavaScript to keep the time-to-interactive close to the first meaningful paint.   
  1. Client-side rendering or server-side rendering?In both scenarios, our goal should be to set up progressive booting: Use server-side rendering to get a quick first meaningful paint, but also include some minimal necessary JavaScript to keep the time-to-interactive close to the first meaningful paint. If JavaScript is coming too late after the First Meaningful Paint, the browser might lock up the main thread while parsing, compiling and executing late-discovered JavaScript, hence handcuffing the interactivity of site or application. To avoid it, always break up the execution of functions into separate, asynchronous tasks, and where possible use requestIdleCallback. Consider lazy loading parts of the UI using WebPack’s dynamic import() supportavoiding the load, parse, and compile cost until the users really need them (thanks Addy!). In its essence, Time to Interactive (TTI) tells us the time between navigation and interactivity. The metric is defined by looking at the first five-second window after the initial content is rendered, in which no JavaScript tasks take longer than 50ms. If a task over 50ms occurs, the search for a five-second window starts over. As a result, the browser will first assume that it reached Interactive, just to switch to Frozen, just to eventually switch back to Interactive. Once we reached Interactive, we can then — either on demand or as time allows — boot non-essential parts of the app. Unfortunately, as Paul Lewis noticedframeworks typically have no concept of priority that can be surfaced to developers, and hence progressive booting is difficult to implement with most libraries and frameworks. If you have the time and resources, use this strategy to ultimately boost performance. So, client-side or server-side? If there is no visible benefit to the user, client-side rendering might not be really necessary — actually, server-side-rendered HTML could be faster. Perhaps you could even pre-render some of your content with static site generators and push them straight to the CDNs, with some JavaScript on top. Limit the use of client-side frameworks to pages that absolutely require them. Server-rendering and client-rendering are a disaster if done poorly. Consider pre-rendering at build time and CSS inlining on the fly to produce production-ready static files. Addy Osmani has given a fantastic talk on the Cost of JavaScript that might be worth watching.
  2. Constrain the impact of third-party scripts.With all performance optimizations in place, often we can’t control third-party scripts coming from business requirements. Third-party-scripts metrics aren’t influenced by end-user experience, so too often one single script ends up calling a long tail of obnoxious third-party scripts, hence ruining a dedicated performance effort. To contain and mitigate performance penalties that these scripts bring along, it’s not enough to just load them asynchronously (probably via defer) and accelerate them via resource hints such as dns-prefetch or preconnect. As Yoav Weiss explained in his must-watch talk on third-party scriptsin many cases these scripts download resources that are dynamic. The resources change between page loads, so we don’t necessarily know which hosts the resources will be downloaded from and what resources they would be. What options do we have then? Consider using service workers by racing the resource download with a timeout and if the resource hasn’t responded within a certain timeout, return an empty response to tell the browser to carry on with parsing of the page. You can also log or block third-party requests that aren’t successful or don’t fulfill certain criteria. If you can, load the 3rd-party-script from your own server rather than from the vendor’s server.
    Casper.com published a detailed case study on how they managed to shave 1.7 seconds off the site by self-hosting Optimizely. It might be worth it. (Image source) (Large preview)
    Another option is to establish a Content Security Policy (CSP) to restrict the impact of third-party scripts, e.g. disallowing the download of audio or video. The best option is to embed scripts via so that the scripts are running in the context of the iframe and hence don’t have access to the DOM of the page, and can’t run arbitrary code on your domain. Iframes can be further constrained using the sandbox attribute, so you can disable any functionality that iframe may do, e.g. prevent scripts from running, prevent alerts, form submission, plugins, access to the top navigation, and so on. For example, it’s probably going to be necessary to allow scripts to run with . Each of the limitations can be lifted via various allow values on the sandbox attribute (supported almost everywhere), so constrain them to the bare minimum of what they should be allowed to do. Consider using Intersection Observer; that would enable ads to be iframed while still dispatching events or getting the information that they need from the DOM (e.g. ad visibility). Watch out for new policies such as Feature policyresource size limits and CPU/Bandwidth priority to limit harmful web features and scripts that would slow down the browser, e.g. synchronous scripts, synchronous XHR requests, document.write and outdated implementations. To stress-test third partiesexamine bottom-up summaries in Performance profile page in DevTools, test what happens if a request is blocked or it has timed out — for the latter, you can use WebPageTest’s Blackhole server blackhole.webpagetest.org that you can point specific domains to in your hosts file. Preferably self-host and use a single hostnamebut also generate a request map that exposes fourth-party calls and detect when the scripts change. You can use Harry Roberts' approach for auditing third parties and produce spreadsheets like this one. Harry also explains the auditing workflow in his talk on third-party performance and auditing.
Image credit: Harry Roberts
  1. Set HTTP cache headers properly.Double-check that expiresmax-agecache-controland other HTTP cache headers have been set properly. In general, resources should be cacheable either for a very short time (if they are likely to change) or indefinitely (if they are static) — you can just change their version in the URL when needed. Disable the Last-Modified header as any asset with it will result in a conditional request with an If-Modified-Since-header even if the resource is in cache. Same with Etag. Use Cache-control: immutabledesigned for fingerprinted static resources, to avoid revalidation (as of December 2018, supported in Firefox, Edge and Safari; in Firefox only on https:// transactions). In fact "across all of the pages in the HTTP Archive, 2% of requests and 30% of sites appear to include at least 1 immutable response. Additionally, most of the sites that are using it have the directive set on assets that have a long freshness lifetime." Remember the stale-while-revalidate? As you probably know, we specify the caching time with the Cache-Control response header, e.g. Cache-Control: max-age=604800. After 604800 seconds have passed, the cache will re-fetch the requested content, causing the page to load slower. This slowdown can be avoided by using stale-while-revalidate; it basically defines an extra window of time during which a cache can use a stale asset as long as it revalidates it async in the background. Thus, it "hides" latency (both in the network and on the server) from clients. In October 2018, Chrome published an intent to ship handling of stale-while-revalidate in HTTP Cache-Control header, so as a result, it should improve subsequent page load latencies as stale assets are no longer in the critical path. Result: zero RTT for repeat views. You can use Heroku’s primer on HTTP caching headersJake Archibald’s "Caching Best Practices" and Ilya Grigorik’s HTTP caching primer as guides. Also, be wary of the vary headerespecially in relation to CDNsand watch out for the Key header which helps avoiding an additional round trip for validation whenever a new request differs slightly (but not significantly) from prior requests (thanks, Guy!). Also, double-check that you aren’t sending unnecessary headers (e.g. x-powered-bypragmax-ua-compatibleexpires and others) and that you include useful security and performance headers (such as Content-Security-PolicyX-XSS-ProtectionX-Content-Type-Options and others). Finally, keep in mind the performance cost of CORS requests in single-page applications.

Delivery Optimizations

  1. Do you load all JavaScript libraries asynchronously?When the user requests a page, the browser fetches the HTML and constructs the DOM, then fetches the CSS and constructs the CSSOM, and then generates a rendering tree by matching the DOM and CSSOM. If any JavaScript needs to be resolved, the browser won’t start rendering the page until it’s resolved, thus delaying rendering. As developers, we have to explicitly tell the browser not to wait and to start rendering the page. The way to do this for scripts is with the defer and async attributes in HTML. In practice, it turns out we should prefer defer to async (at a cost to users of Internet Explorer up to and including version 9, because you’re likely to break scripts for them). According to Steve Soudersonce async scripts arrive, they are executed immediately. If that happens very fast, for example when the script is in cache aleady, it can actually block HTML parser. With deferbrowser doesn’t execute scripts until HTML is parsed. So, unless you need JavaScript to execute before start render, it’s better to use defer. Also, as mentioned above, limit the impact of third-party libraries and scripts, especially with social sharing buttons and embeds (such as maps). Size Limit helps you prevent JavaScript libraries bloat: If you accidentally add a large dependency, the tool will inform you and throw an error. You can use static social sharing buttons (such as by SSBG) and static links to interactive maps instead. You might want to revise your non-blocking script loader for CSP compliance.
  2. Lazy load expensive components with IntersectionObserver.In general, it’s a good idea to lazy-load all expensive components, such as heavy JavaScript, videos, iframes, widgets, and potentially images. The most performant way to do so is by using the Intersection Observer API that provides a way to asynchronously observe changes in the intersection of a target element with an ancestor element or with a top-level document’s viewport. Basically, you need to create a new IntersectionObserver object, which receives a callback function and a set of options. Then we add a target to observe. The callback function executes when the target becomes visible or invisible, so when it intercepts the viewport, you can start taking some actions before the element becomes visible. In fact, we have a granular control over when the observer’s callback should be invoked, with rootMargin (margin around the root) and threshold (a single number or an array of numbers which indicate at what percentage of the target’s visibility we are aiming). Alejandro Garcia Anglada has published a handy tutorial on how to actually implement it, Rahul Nanwani wrote a detailed post on lazy-loading foreground and background imagesand Google Fundamentals provide a detailed tutorial on lazy loading images and video with Intersection Observer as well. Remember art-directed storytelling long reads with moving and sticky objects? You can implement performant scrollytelling with Intersection Observertoo. Also, watch out for the lazyload attribute that will allow us to specify which images and iframes should be lazy loaded, natively. Feature policy: LazyLoad will provide a mechanism that allows us to force opting in or out of LazyLoad functionality on a per-domain basis (similar to how Content Security Policies work). Bonus: once shipped, priority hints will allow us to specify importance on scripts and preloads in the header as well (currently in Chrome Canary).
  3. Load images progressively.You could even take lazy loading to the next level by adding progressive image loading to your pages. Similarly to Facebook, Pinterest and Medium, you could load low quality or even blurry images first, and then as the page continues to load, replace them with the full quality versions by using the LQIP (Low Quality Image Placeholders) technique proposed by Guy Podjarny. Opinions differ if these techniques improve user experience or not, but it definitely improves time to first meaningful paint. We can even automate it by using SQIP that creates a low quality version of an image as an SVG placeholder, or Gradient Image Placeholders with CSS linear gradients. These placeholders could be embedded within HTML as they naturally compress well with text compression methods. In his article, Dean Hume has described how this technique can be implemented using Intersection Observer. Browser support? Decentwith Chrome, Firefox, Edge and Samsung Internet being on board. WebKit status is currently supported in preview. Fallback? If the browser doesn’t support intersection observer, we can still lazy load a polyfill or load the images immediately. And there is even a library for it. Want to go fancier? You could trace your images and use primitive shapes and edges to create a lightweight SVG placeholder, load it first, and then transition from the placeholder vector image to the (loaded) bitmap image.
  4. SVG lazy loading technique by José M. Pérez. (Large preview)
  5. Do you send critical CSS?To ensure that browsers start rendering your page as quickly as possible, it’s become a common practice to collect all of the CSS required to start rendering the first visible portion of the page (known as "critical CSS" or "above-the-fold CSS") and add it inline in the of the page, thus reducing roundtrips. Due to the limited size of packages exchanged during the slow start phase, your budget for critical CSS is around 14 KB. If you go beyond that, the browser will need additional roundtrips to fetch more styles. CriticalCSS and Critical enable you to do just that. You might need to do it for every template you’re using. If possible, consider using the conditional inlining approach used by the Filament Group, or convert inline code to static assets on the fly. With HTTP/2, critical CSS could be stored in a separate CSS file and delivered via a server push without bloating the HTML. The catch is that server pushing is troublesome with many gotchas and race conditions across browsers. It isn’t supported consistently and has some caching issues (see slide 114 onwards of Hooman Beheshti’s presentation). The effect could, in fact, be negative and bloat the network buffers, preventing genuine frames in the document from being delivered. Also, it appears that server pushing is much more effective on warm connections due to the TCP slow start. Even with HTTP/1, putting critical CSS in a separate file on the root domain has benefitssometimes even more than inlining due to caching. Chrome speculatively opens a second HTTP connection to the root domain when requesting the page, which removes the need for a TCP connection to fetch this CSS (thanks, Philip!) A few gotchas to keep in mind: unlike preload that can trigger preload from any domain, you can only push resources from your own domain or domains you are authoritative for. It can be initiated as soon as the server gets the very first request from the client. Server pushed resources land in the Push cache and are removed when the connection is terminated. However, since an HTTP/2 connection can be re-used across multiple tabs, pushed resources can be claimed by requests from other tabs as well (thanks, Inian!). At the moment, there is no simple way for the server to know if pushed resources are already in one of the user’s cachesso resources will keep being pushed with every user’s visit. You may then need to create a cache-aware HTTP/2 server push mechanism. If fetched, you could try to get them from a cache based on the index of what’s already in the cache, avoiding secondary server pushes altogether. Keep in mind, though, that the new cache-digest specification negates the need to manually build such "cache-aware" servers, basically declaring a new frame type in HTTP/2 to communicate what’s already in the cache for that hostname. As such, it could be particularly useful for CDNs as well. For dynamic content, when a server needs some time to generate a response, the browser isn’t able to make any requests since it’s not aware of any sub-resources that the page might reference. For that case, we can warm up the connection and increase the TCP congestion window size, so that future requests can be completed faster. Also, all inlined assets are usually good candidates for server pushing. In fact, Inian Parameshwaran did remarkable research comparing HTTP/2 Push vs. HTTP Preloadand it’s a fantastic read with all the details you might need. Server Push or Not Server Push? Colin Bendell’s Should I Push? might point you in the right direction. Bottom line: As Sam Saccone notedpreload is good for moving the start download time of an asset closer to the initial request, while Server Push is good for cutting out a full RTT (or moredepending on your server think time) — if you have a service worker to prevent unnecessary pushing, that is.
  6. Experiment with regrouping your CSS rules.We’ve got used to critical CSS, but there are a few optimizations that could go beyond that. Harry Roberts conducted a remarkable research with quite surprising results. For example, it might be a good idea to split the main CSS file out into its individual media queries. That way, the browser will retrieve critical CSS with high priority, and everything else with low priority — completely off the critical path. Also, avoid placing before async snippets. If scripts don’t depend on stylesheets, consider placing blocking scripts above blocking styles. If they do, split that JavaScript in two and load it either side of your CSS. Scott Jehl solved another interesting problem by caching an inlined CSS file with a service workera common problem familiar if you’re using critical CSS. Basically, we add an ID attribute onto the style element so that it’s easy to find it using JavaScript, then a small piece of JavaScript finds that CSS and uses the Cache API to store it in a local browser cache (with a content type of text/css) for use on subsequent pages. To avoid inlining on subsequent pages and instead reference the cached assets externally, we then set a cookie on the first visit to a site. Voilà!
Do we stream reponses? With streaming, HTML rendered during the initial navigation request can take full advantage of the browser’s streaming HTML parser.
  1. Do you stream responses?Often forgotten and neglected, streams provide an interface for reading or writing asynchronous chunks of data, only a subset of which might be available in memory at any given time. Basically, they allow the page that made the original request to start working with the response as soon as the first chunk of data is available, and use parsers that are optimized for streaming to progressively display the content. We could create one stream from multiple sources. For example, instead of serving an empty UI shell and letting JavaScript populate it, you can let the service worker construct a stream where the shell comes from a cache, but the body comes from the network. As Jeff Posnick notedif your web app is powered by a CMS that server-renders HTML by stitching together partial templates, that model translates directly into using streaming responses, with the templating logic replicated in the service worker instead of your server. Jake Archibald’s The Year of Web Streams article highlights how exactly you could build it. Performance boost is quite noticeable. One important advantage of streaming the entire HTML response is that HTML rendered during the initial navigation request can take full advantage of the browser’s streaming HTML parser. Chunks of HTML that are inserted into a document after the page has loaded (as is common with content populated via JavaScript) can’t take advantage of this optimization. Browser support? Getting there with Chrome 52+, Firefox 57+ (behind flag), Safari and Edge supporting the API and Service Workers being supported in all modern browsers.
  2. Consider making your components connection-aware.Data can be expensive and with growing payload, we need to respect users who choose to opt into data savings while accessing our sites or apps. The Save-Data client hint request header allows us to customize the application and the payload to cost- and performance-constrained users. In fact, you could rewrite requests for high DPI images to low DPI imagesremove web fonts, fancy parallax effects, preview thumbnails and infinite scroll, turn off video autoplay, server pushes, reduce the number of displayed items and downgrade image quality, or even change how you deliver markup. Tim Vereecke has published a very detailed article on data-s(h)aver strategies featuring many options for data saving. The header is currently supported only in Chromium, on the Android version of Chrome or via the Data Saver extension on a desktop device. Finally, you can also use the Network Information API to deliver low/high resolution images and videos based on the network type. Network Information API and specifically navigator.connection.effectiveType (Chrome 62+) use RTTdownlinkeffectiveType values (and a few others) to provide a representation of the connection and the data that users can handle. In this context, Max Stoiber speaks of connection-aware components. For example, with React, we could write a component that renders different elements for different connection types. As Max suggested, a component in a news article might output:
    • Offline: a placeholder with alt text,
    • 2G / save-data mode: a low-resolution image,
    • 3G on non-Retina screen: a mid-resolution image,
    • 3G on Retina screens: high-res Retina image,
    • 4G: an HD video.
    Dean Hume provides a practical implementation of a similar logic using a service worker. For a video, we could display a video poster by default, and then display the "Play" icon as well as the video player shell, meta-data of the video etc. on better connections. As a fallback for non-supporting browsers, we could listen to canplaythrough event and use Promise.race() to timeout the source loading if the canplaythrough event doesn’t fire within 2 seconds.
  3. Consider making your components device memory-aware.Network connection gives us only one perspective at the context of the user though. Going further, you could also dynamically adjust resources based on available device memorywith the Device Memory API (Chrome 63+). navigator.deviceMemory returns how much RAM the device has in gigabytes, rounded down to the nearest power of two. The API also features a Client Hints Header, Device-Memorythat reports the same value.
The 'Priority' column in DevTools. Image credit: Ben Schwarz, The Critical Request
  1. Warm up the connection to speed up delivery.Use resource hints to save time on dns-prefetch (which performs a DNS lookup in the background), preconnect (which asks the browser to start the connection handshake (DNS, TCP, TLS) in the background), prefetch (which asks the browser to request a resource) and preload (which prefetches resources without executing them, among other things). Most of the time these days, we’ll be using at least preconnect and dns-prefetchand we’ll be cautious with using prefetch and preload; the former should only be used if you are confident about what assets the user will need next (for example, in a purchasing funnel). Note that even with preconnect and dns-prefetchthe browser has a limit on the number of hosts it will look up/connect to in parallel, so it’s a safe bet to order them based on priority (thanks Philip!). In fact, using resource hints is probably the easiest way to boost performance, and it works well indeed. When to use what? As Addy Osmani has explainedwe should preload resources that we have high-confidence will be used in the current page. Prefetch resources likely to be used for future navigations across multiple navigation boundaries, e.g. Webpack bundles needed for pages the user hasn’t visited yet. Addy’s article on "Loading Priorities in Chrome" shows how exactly Chrome interprets resource hints, so once you’ve decided which assets are critical for rendering, you can assign high priority to them. To see how your requests are prioritized, you can enable a "priority" column in the Chrome DevTools network request table (as well as Safari Technology Preview). For example, since fonts usually are important assets on a page, it’s always a good idea to request the browser to download fonts with preload. You could also load JavaScript dynamicallyeffectively lazy-loading execution. Also, since accepts a media attribute, you could choose to selectively prioritize resources based on @media query rules. A few gotchas to keep in mind: preload is good for moving the start download time of an asset closer to the initial request, but preloaded assets land in the memory cache which is tied to the page making the request. preload plays well with the HTTP cache: a network request is never sent if the item is already there in the HTTP cache. Hence, it’s useful for late-discovered resources, a hero image loaded via background-image, inlining critical CSS (or JavaScript) and pre-loading the rest of the CSS (or JavaScript). Also, a preload tag can initiate a preload only after the browser has received the HTML from the server and the lookahead parser has found the preload tag. Preloading via the HTTP header is a bit faster since we don’t to wait for the browser to parse the HTML to start the request. Early Hints will help even further, enabling preload to kick in even before the response headers for the HTML are sent and Priority Hints (coming soon) will help us indicate loading priorities for scripts. Beware: if you’re using preloadas must be defined or nothing loadsplus preloaded fonts without the crossorigin attribute will double fetch.
  2. Use service workers for caching and network fallbacks.No performance optimization over a network can be faster than a locally stored cache on a user’s machine. If your website is running over HTTPS, use the "Pragmatist’s Guide to Service Workers" to cache static assets in a service worker cache and store offline fallbacks (or even offline pages) and retrieve them from the user’s machine, rather than going to the network. Also, check Jake’s Offline Cookbook and the free Udacity course "Offline Web Applications." Browser support? As stated above, it’s widely supported (Chrome, Firefox, Safari TP, Samsung Internet, Edge 17+) and the fallback is the network anyway. Does it help boost performance? Oh yes, it does. And it’s getting better, e.g. with Background Fetch allowing background uploads/downloads from a service worker. Shipped in Chrome 71. There are a number of use cases for a service worker. For example, you could implement "Save for offline" featurehandle broken imagesintroduce messaging between tabs or provide different caching strategies based on request types. In general, a common reliable strategy is to store the app shell in the service worker’s cache along with a few critical pages, such as offline page, frontpage and anything else that might be important in your case. There are a few gotchas to keep in mind though. With a service worker in place, we need to beware range requests in Safari (if you are using Workbox for a service worker it has a range request module). If you ever stumbled upon DOMException: Quota exceeded. error in the browser console, then look into Gerardo’s article When 7KB equals 7MB. As Gerardo writes, “If you are building a progressive web app and are experiencing bloated cache storage when your service worker caches static assets served from CDNs, make sure the proper CORS response header exists for cross-origin resources, you do not cache opaque responses with your service worker unintentionally, you opt-in cross-origin image assets into CORS mode by adding the crossorigin attribute to the tag.” A good starting point for using service workers would be Workboxa set of service worker libraries built specifically for building progressive web apps.
  3. Are you using service workers on the CDN/Edge, e.g. for A/B testing?At this point, we are quite used to running service workers on the client, but with CDNs implementing them on the serverwe could use them to tweak performance on the edge as well. For example, in A/B tests, when HTML needs to vary its content for different users, we could use Service Workers on the CDN servers to handle the logic. We could also stream HTML rewriting to speed up sites that use Google Fonts.
  4. Optimize rendering performance.Isolate expensive components with CSS containment — for example, to limit the scope of the browser’s styles, of layout and paint work for off-canvas navigation, or of third-party widgets. Make sure that there is no lag when scrolling the page or when an element is animated, and that you’re consistently hitting 60 frames per second. If that’s not possible, then at least making the frames per second consistent is preferable to a mixed range of 60 to 15. Use CSS’ will-change to inform the browser of which elements and properties will change. Also, measure runtime rendering performance (for example, in DevTools). To get started, check Paul Lewis’ free Udacity course on browser-rendering optimization and Georgy Marchuk’s article on Browser painting and considerations for web performance. If you want to dive deeper into the topic, Nolan Lawson has shared tricks to accurately measure layout performance in his article, and Jason Miller suggested alternative techniques, too. We also have a lil' article by Sergey Chikuyonok on how to get GPU animation right. Quick note: changes to GPU-composited layers are the least expensiveso if you can get away by triggering only compositing via opacity and transformyou'll be on the right track. Anna Migas has provided a lot of practical advice in her talk on Debugging UI Rendering Performancetoo.
  5. Have you optimized rendering experience?While the sequence of how components appear on the page, and the strategy of how we serve assets to the browser matter, we shouldn’t underestimate the role of perceived performancetoo. The concept deals with psychological aspects of waiting, basically keeping customers busy or engaged while something else is happening. That’s where perception managementpreemptive startearly completion and tolerance management come into play. What does it all mean? While loading assets, we can try to always be one step ahead of the customer, so the experience feels swift while there is quite a lot happening in the background. To keep the customer engaged, we can test skeleton screens (implementation demo) instead of loading indicators, add transitions/animations and basically cheat the UX when there is nothing more to optimize. Beware though: skeleton screens should be tested before deploying as some tests showed that skeleton screens can perform the worst by all metrics.

HTTP/2

  1. Migrate to HTTPS, then turn on HTTP/2.With Google moving towards a more secure web and eventual treatment of all HTTP pages in Chrome as being "not secure," a switch to HTTP/2 environment is unavoidable. HTTP/2 is supported very well; it isn’t going anywhere; and, in most cases, you’re better off with it. Once running on HTTPS already, you can get a major performance boost with service workers and server push (at least long term).
    Eventually, Google plans to label all HTTP pages as non-secure, and change the HTTP security indicator to the red triangle that Chrome uses for broken HTTPS. (Image source)
    The most time-consuming task will be to migrate to HTTPSand depending on how large your HTTP/1.1 user base is (that is, users on legacy operating systems or with legacy browsers), you’ll have to send a different build for legacy browsers performance optimizations, which would require you to adapt to a different build process. Beware: Setting up both migration and a new build process might be tricky and time-consuming. For the rest of this article, I’ll assume that you’re either switching to or have already switched to HTTP/2.
  2. Properly deploy HTTP/2.Again, serving assets over HTTP/2 requires a partial overhaul of how you’ve been serving assets so far. You’ll need to find a fine balance between packaging modules and loading many small modules in parallel. At the end of the day, still the best request is no requesthowever, the goal is to find a fine balance between quick first delivery of assets and caching. On the one hand, you might want to avoid concatenating assets altogether, instead breaking down your entire interface into many small modules, compressing them as a part of the build process, referencing them via the "scout" approach and loading them in parallel. A change in one file won’t require the entire style sheet or JavaScript to be re-downloaded. It also minimizes parsing time and keeps the payloads of individual pages low. On the other hand, packaging still matters. First, compression will suffer. The compression of a large package will benefit from dictionary reuse, whereas small separate packages will not. There’s standard work to address that, but it’s far out for now. Secondly, browsers have not yet been optimized for such workflows. For example, Chrome will trigger inter-process communications (IPCs) linear to the number of resources, so including hundreds of resources will have browser runtime costs.
    To achieve best results with HTTP/2, consider to load CSS progressivelyas suggested by Chrome’s Jake Archibald.
    Still, you can try to load CSS progressively. In fact, since Chrome 69, in-body CSS no longer blocks rendering for Chrome. Obviously, by doing so, you are actively penalizing HTTP/1.1 users, so you might need to generate and serve different builds to different browsers as part of your deployment process, which is where things get slightly more complicated. You could get away with HTTP/2 connection coalescingwhich allows you to use domain sharding while benefiting from HTTP/2, but achieving this in practice is difficult, and in general, it’s not considered to be good practice. What to do? Well, if you’re running over HTTP/2, sending around 6–10 packages seems like a decent compromise (and isn’t too bad for legacy browsers). Experiment and measure to find the right balance for your website.
  3. Do your servers and CDNs support HTTP/2?Different servers and CDNs are probably going to support HTTP/2 differently. Use Is TLS Fast Yet? to check your options, or quickly look up how your servers are performing and which features you can expect to be supported. Consult Pat Meenan’s incredible research on HTTP/2 priorities and test server support for HTTP/2 prioritization. According to Pat, it’s recommended to enable BBR congestion control and set tcp_notsent_lowat to 16KB for HTTP/2 prioritization to work reliably on Linux 4.9 kernels and later (thanks, Yoav!). Andy Davies did a similar research for HTTP/2 prioritization across browsers, CDNs and Cloud Hosting Services.
Is TLS Fast Yet? allows you to check your options for servers and CDNs when switching to HTTP/2. (Large preview)
  1. Is OCSP stapling enabled?By enabling OCSP stapling on your serveryou can speed up your TLS handshakes. The Online Certificate Status Protocol (OCSP) was created as an alternative to the Certificate Revocation List (CRL) protocol. Both protocols are used to check whether an SSL certificate has been revoked. However, the OCSP protocol does not require the browser to spend time downloading and then searching a list for certificate information, hence reducing the time required for a handshake.
  2. Have you adopted IPv6 yet?Because we’re running out of space with IPv4 and major mobile networks are adopting IPv6 rapidly (the US has reached a 50% IPv6 adoption threshold), it’s a good idea to update your DNS to IPv6 to stay bulletproof for the future. Just make sure that dual-stack support is provided across the network — it allows IPv6 and IPv4 to run simultaneously alongside each other. After all, IPv6 is not backwards-compatible. Also, studies show that IPv6 made those websites 10 to 15% faster due to neighbor discovery (NDP) and route optimization.
  3. Is HPACK compression in use?If you’re using HTTP/2, double-check that your servers implement HPACK compression for HTTP response headers to reduce unnecessary overhead. Because HTTP/2 servers are relatively new, they may not fully support the specification, with HPACK being an example. H2spec is a great (if very technically detailed) tool to check that. HPACK’s compression algorithm is quite impressiveand it works.
  4. Make sure the security on your server is bulletproof.All browser implementations of HTTP/2 run over TLS, so you will probably want to avoid security warnings or some elements on your page not working. Double-check that your security headers are set properlyeliminate known vulnerabilitiesand check your certificate. Also, make sure that all external plugins and tracking scripts are loaded via HTTPS, that cross-site scripting isn’t possible and that both HTTP Strict Transport Security headers and Content Security Policy headers are properly set.

Testing And Monitoring

  1. Have you optimized your auditing workflow?It might not sound like a big deal, but having the right settings in place at your fingertips might save you quite a bit of time in testing. Consider using Tim Kadlec’s Alfred Workflow for WebPageTest for submitting a test to the public instance of WebPageTest. You could also drive WebPageTest from a Google Spreadsheet and incorporate accessibility, performance and SEO scores into your Travis setup with Lighthouse CI or straight into Webpack. And if you need to debug something quickly but your build process seems to be remarkably slow, keep in mind that "whitespace removal and symbol mangling accounts for 95% of the size reduction in minified code for most JavaScript — not elaborate code transforms. You can simply disable compression to speed up Uglify builds by 3 to 4 times."
Integrating accessibility, performance and SEO scores into your Travis setup with Lighthouse CI will highlight the performance impact of a new feature to all contributing developers. (Image source) (Large preview)
  1. Have you tested in proxy browsers and legacy browsers?Testing in Chrome and Firefox is not enough. Look into how your website works in proxy browsers and legacy browsers. UC Browser and Opera Mini, for instance, have a significant market share in Asia (up to 35% in Asia). Measure average Internet speed in your countries of interest to avoid big surprises down the road. Test with network throttling, and emulate a high-DPI device. BrowserStack is fantastic, but test on real devices as well.
k6 allows you write unit tests-alike performance tests.
  1. Have you tested the accessibility performance?When the browser starts to load a page, it builds a DOM, and if there is an assistive technology like a screen reader running, it also creates an accessibility tree. The screen reader then has to query the accessibility tree to retrieve the information and make it available to the user — sometimes by default, and sometimes on demand. And sometimes it takes time. When talking about fast Time to Interactive, usually we mean an indicator of how soon a user can interact with the page by clicking or tapping on links and buttons. The context is slightly different with screen readers. In that case, fast Time to Interactive means how much time passes by until the screen reader can announce navigation on a given page and a screen reader user can actually hit keyboard to interact. Léonie Watson has given an eye-opening talk on accessibility performance and specifically the impact slow loading has on screen reader announcement delays. Screen readers are used to fast-paced announcements and quick navigation, and therefore might potentially be even less patient than sighted users. Large pages and DOM manipulations with JavaScript will cause delays in screen reader announcements. A rather unexplored area that could use some attention and testing as screen readers are available on literally every platform (Jaws, NVDA, Voiceover, Narrator, Orca).
  2. Is continuous monitoring set up?Having a private instance of WebPagetest is always beneficial for quick and unlimited tests. However, a continuous monitoring tool — like SitespeedCalibre and SpeedCurve — with automatic alerts will give you a more detailed picture of your performance. Set your own user-timing marks to measure and monitor business-specific metrics. Also, consider adding automated performance regression alerts to monitor changes over time. Look into using RUM-solutions to monitor changes in performance over time. For automated unit-test-alike load testing tools, you can use k6 with its scripting API. Also, look into SpeedTrackerLighthouse and Calibre.

Quick Wins

This list is quite comprehensive, and completing all of the optimizations might take quite a while. So, if you had just 1 hour to get significant improvements, what would you do? Let’s boil it all down to 12 low-hanging fruits. Obviously, before you start and once you finish, measure results, including start rendering time and Speed Index on a 3G and cable connection.
  1. Measure the real world experience and set appropriate goals. A good goal to aim for is First Meaningful Paint < 1 s, a Speed Index value < 1250, Time to Interactive < 5s on slow 3G, for repeat visits, TTI < 2s. Optimize for start rendering time and time-to-interactive.
  2. Prepare critical CSS for your main templates, and include it in the of the page. (Your budget is 14 KB). For CSS/JS, operate within a critical file size budget of max. 170KB gzipped (0.7MB decompressed).
  3. Trim, optimize, defer and lazy-load as many scripts as possible, check lightweight alternatives and limit the impact of third-party scripts.
  4. Serve legacy code only to legacy browsers with .
  5. Experiment with regrouping your CSS rules and test in-body CSS.
  6. Add resource hints to speed up delivery with faster dns-lookuppreconnectprefetch and preload.
  7. Subset web fonts and load them asynchronously, and utilize font-display in CSS for fast first rendering.
  8. Optimize images, and consider using WebP for critical pages (such as landing pages).
  9. Check that HTTP cache headers and security headers are set properly.
  10. Enable Brotli or Zopfli compression on the server. (If that’s not possible, don’t forget to enable Gzip compression.)
  11. If HTTP/2 is available, enable HPACK compression and start monitoring mixed-content warnings. Enable OCSP stapling.
  12. Cache assets such as fonts, styles, JavaScript and images in a service worker cache.

Download The Checklist (PDF, Apple Pages)

With this checklist in mind, you should be prepared for any kind of front-end performance project. Feel free to download the print-ready PDF of the checklist as well as an editable Apple Pages document to customize the checklist for your needs: If you need alternatives, you can also check the front-end checklist by Dan Rublicthe "Designer’s Web Performance Checklist" by Jon Yablonski and the FrontendChecklist.

Off We Go!

Some of the optimizations might be beyond the scope of your work or budget or might just be overkill given the legacy code you have to deal with. That’s fine! Use this checklist as a general (and hopefully comprehensive) guide, and create your own list of issues that apply to your context. But most importantly, test and measure your own projects to identify issues before optimizing. Happy performance results in 2019, everyone!
A huge thanks to Guy Podjarny, Yoav Weiss, Addy Osmani, Artem Denysov, Denys Mishunov, Ilya Pukhalski, Jeremy Wagner, Colin Bendell, Mark Zeman, Patrick Meenan, Leonardo Losoviz, Andy Davies, Rachel Andrew, Anselm Hannemann, Patrick Hamann, Andy Davies, Tim Kadlec, Rey Bango, Matthias Ott, Peter Bowyer, Phil Walton, Mariana Peralta, Philipp Tellis, Ryan Townsend, Ingrid Bergman, Mohamed Hussain S. H., Jacob Groß, Tim Swalling, Bob Visser, Kev Adamson, Adir Amsalem, Aleksey Kulikov and Rodney Rehm for reviewing this article, as well as our fantastic community which has shared techniques and lessons learned from its work in performance optimization for everybody to use. You are truly smashing!
(ra, il)




Source link
Quitter la version mobile