Comment Gatsby a contourné Next.js

L'auteur de l'article, dont nous publions la traduction aujourd'hui, travaille comme programmeur à Antler. Cette société est un générateur mondial de startups. Il y a plusieurs jours de démonstration à Antler plusieurs fois par an, réunissant de nombreux créateurs de startups et investisseurs du monde entier. La situation autour de COVID-19 a forcé Antler à traduire ses événements dans un format en ligne.



L'entreprise voulait s'assurer que les visiteurs de leurs événements virtuels, sans être distraits par quoi que ce soit, et ne rester coincés nulle part, verraient la chose la plus importante. À savoir, les idées de startups présentées au public, exprimées comme le contenu de pages Web. Les journées de démonstration virtuelle peuvent intéresser un public assez large. Certains membres de ce public peuvent participer pour la première fois à quelque chose comme ça. Par conséquent, l'entreprise devait tout faire de la meilleure façon possible et fournir un chargement à grande vitesse des pages représentant les startups. Ils ont décidé que ce n'est que le cas lorsqu'une application Web progressive haute performance (PWA, Progressive Web App) peut être utile. Le principal problème était de trouver la bonne technologie pour développer la PWA.


Rendu de serveur ou générateur de site statique?


Pour commencer, je vais vous présenter un peu le cours. Tous nos projets sont basés sur React et la bibliothèque Material-UI. En conséquence, nous avons initialement décidé de ne pas s'écarter de cette pile technologique, ce qui nous permettrait d'assurer une vitesse de développement élevée et de rendre le nouveau projet compatible avec ce que nous avons déjà. La principale différence entre ce nouveau projet et nos autres applications React est que la base de données pour eux a été créée à l'aide de l'application create-react et qu'ils ont été entièrement rendus sur le client (CSR, Client-Side Rendering). Cela a notamment conduit au fait que lors du chargement initial de l'application, les utilisateurs étaient obligés d'observer un écran blanc vierge pendant le chargement, le traitement et l'exécution du code JavaScript du projet.

Nous avions besoin d'un niveau de performance sans compromis. Par conséquent, nous avons commencé à penser à utiliser le rendu côté serveur (SSR, rendu côté serveur) ou un générateur de site statique (SSG, Static Site Generator) afin que le chargement initial des applications soit le plus rapide possible.

Nos données sont stockées dans Cloud Firestore, et nous y accédons en utilisant Algolia. Cela nous permet de contrôler, au niveau du champ de la base de données, l'accès public aux données avec des clés API restreintes. Cela améliore également les performances des requêtes. Par expérience, nous savons que les requêtes Algolia sont plus rapides que la normale et que le SDK JavaScript Firestore compressé a une taille de 86 Ko . Dans le cas de l'Algolie, c'est 7,5 Kb .

De plus, nous voulions rendre les données que nous donnons aux clients aussi fraîches que possible. Cela nous aiderait à corriger très rapidement des données erronées qui pourraient être publiées accidentellement. Alors que la pratique standard du SSG prévoit la mise en œuvre de demandes de données pertinentes lors de l'assemblage du projet, nous nous attendions à ce que dans notre base de données les données soient écrites assez souvent. En particulier, nous parlons d'un enregistrement de données initié par des administrateurs utilisant l'interface firetable, et à l'initiative des fondateurs des projets utilisant le portail web. Cela conduit à un montage compétitif du projet. De plus, en raison des caractéristiques structurelles de notre base de données, des modifications mineures peuvent conduire à de nouvelles opérations d'assemblage de projet. Cela rend notre pipeline CI / CD extrêmement inefficace. Par conséquent, nous avions besoin que les demandes de réception de données du référentiel soient exécutées chaque fois qu'un utilisateur demande le chargement d'une page. Malheureusement, cela signifiait que notre solution ne serait pas un exemple de projet SSG «propre».

Initialement, notre application a été créée sur la base de Gatsby, car nous utilisions déjà des landing pages construites sur Gatsby, et sur l'une d'elles, la bibliothèque Material-UI était déjà utilisée. La première version du projet a formé une page qui, pendant le chargement des données, affichait un "squelette". Dans le même temps, la première peinture contentieuse (FCP) était de l'ordre de 1 seconde.


Téléchargement du "squelette" de la page avec chargement ultérieur des données

La solution s'est avérée intéressante, mais elle avait ses inconvénients, car les données de sortie de la page ont été téléchargées à l'initiative du client:

  • Pour voir le contenu de la page, les utilisateurs devraient attendre le téléchargement de cette page elle-même et les données qui y sont affichées, obtenues à travers 4 demandes à l'Algolia.
  • JS- . , React «» . DOM.
  • . , , .

En conséquence, au cours du long week-end, j'ai décidé d'expérimenter avec la version SSR du projet créé à l'aide de Next.js. Heureusement pour moi, la documentation de Material-UI avait un exemple de projet pour Next.js. Par conséquent, je n'avais pas besoin d'apprendre tout ce framework à partir de zéro. Je viens de parcourir certaines parties du didacticiel et de la documentation. J'ai converti l'application en un projet rendu sur le serveur. Lorsqu'un utilisateur a demandé un chargement de page, le serveur a exécuté les demandes de données nécessaires pour remplir la page. Cette étape nous a permis de résoudre les trois problèmes ci-dessus. Voici les résultats des tests pour deux options d'application.


Résultats de la recherche d'applications à l'aide de Google PageSpeed ​​Insights . A gauche se trouve Gatsby (SSG), à droite se trouve Next.js (SSR) ( image originale )

Le FCP pour la version Next.js du projet était environ 3 fois plus élevé que pour sa version basée sur Gatsby. La version Gatsby du projet avait un indice de vitesse de 3,3 secondes, tandis que la version Next.js avait 6,2 secondes. Le temps jusqu'au premier octet (TTFB, Time To First Byte) était de 2,56 secondes lors de l'utilisation de Next.js et de 10-20 ms lors de l'utilisation de Gatsby.

Il convient de noter que la version Next.js du site a été déployée sur un autre service (ici, nous avons utilisé les services d'hébergement ZEIT Now et Firebase - cela pourrait également affecter l'augmentation du TTFB). Mais, malgré cela, il était clair que le transfert des opérations de téléchargement de données vers le serveur faisait apparaître le site plus lentement, malgré le fait que tous les documents de la page étaient chargés à peu près en même temps. Le fait est que dans la version Next.js du projet, l'utilisateur ne voit pendant un certain temps qu'une page blanche vierge.


Capture d'écran montrant le chargement de deux versions d'une application. Le téléchargement ne s'est pas terminé en même temps. Les enregistrements sont synchronisés dès que vous appuyez sur la touche Entrée.

Tout cela nous donne une leçon importante dans le domaine du développement Web: vous devez donner aux utilisateurs un retour visuel. Une étude a révélé que les applications qui utilisent des écrans squelettiques semblent se charger plus rapidement.

Ce résultat, d'ailleurs, ne correspond pas à l'ambiance que vous auriez pu ressentir si vous lisiez des articles sur le développement web au cours des dernières années. À savoir, nous parlons du fait qu'il n'y a rien de mal à utiliser les ressources du client et que le SSR n'est pas une solution complète aux problèmes de performances.

Performances de génération de site statique: comparaison de Gatsby et Next.js


Alors que les deux cadres à l'étude, Gatsby et Next.js, sont connus respectivement pour leur capacité à générer des sites statiques et le rendu de serveur, la prise en charge de SSG a été améliorée dans Next.js 9.3 , ce qui en fait un concurrent de Gatsby.

Au moment d'écrire ces lignes, la capacité de Next.js à générer des sites statiques était encore très récente. Elle avait un peu plus d'un mois. Elle est toujours signalée sur la première page du projet. Maintenant, il n'y a pas beaucoup de comparaisons des capacités SSG de Gatsby et Next.js (ou peut-être qu'il n'y a pas encore de telles comparaisons). En conséquence, j'ai décidé de mener ma propre expérience.

J'ai rendu la version Gatsby du projet à l'état lorsque les données ont été téléchargées sur le client, et j'ai fait en sorte que les deux versions de l'application aient exactement le même ensemble de fonctionnalités. À savoir, j'ai dû supprimer ce dont les plugins Gatsby sont responsables: fonctions SEO, génération de favicons, manifeste PWA. Afin de comparer exclusivement les bundles JavaScript créés par les frameworks, je n'ai pas inclus dans les projets des images et autres contenus téléchargés depuis des sources externes. Les deux versions de l'application ont été déployées sur la plate-forme d'hébergement Firebase. Pour référence, deux versions de l'application ont été créées sur la base de Gatsby 2.20.9 et Next.js 9.3.4.

J'ai exécuté Lighthouse sur mon ordinateur 6 fois pour chaque version. Les résultats ont montré un léger avantage pour Gatsby.


Valeurs moyennes obtenues après 6 lancements Lighthouse pour chaque framework ( image originale )

En termes d'évaluation globale des performances, la version Next.js n'était que légèrement derrière la version Gatsby. Il en va de même pour le FCP et l'indice de vitesse. Le Next First Potential First Input Delay pour la version Next.js de l'application est légèrement plus élevé que pour la version Gatsby.

Afin de mieux comprendre ce qui se passe, je me suis tourné vers l'onglet Réseau des outils de développement Chrome. Il s'est avéré que dans la version Next.js du projet, le nombre de fragments dans lesquels le code JavaScript est divisé est 3 de plus que dans la version Gatsby (à l'exclusion des fichiers manifestes), mais le code compressé est 20 Ko plus petit. Les demandes supplémentaires nécessaires pour télécharger ces fichiers peuvent-elles l'emporter sur les avantages d'un ensemble plus petit au point de nuire aux performances?


Dans la version Gatsby du projet, 7 requêtes sont exécutées pour télécharger 379 Ko de données. Dans la version Next.js du projet - 12 demandes de téléchargement de 359 Ko de données ( image d'origine )

Si vous analysez les performances JavaScript, les outils de développement indiquent que la version Next.js du projet a besoin de 300 ms supplémentaires pour le premier rendu, et que cette version passe plus de temps sur la tâche Évaluer le script. Dans les outils du développeur, cette tâche a même été marquée comme une «tâche longue».


Analyse des performances des différentes options de projet à l'aide de l'onglet Performances des outils de développement Chrome ( image d'origine )

J'ai comparé le code du projet pour savoir s'il existe des différences dans leur implémentation qui pourraient affecter les performances. À l'exception de la suppression du code inutile et des corrections associées aux types TypeScript manquants, la seule différence était la mise en œuvre d'un défilement fluide de la page lors du déplacement vers ses parties individuelles. Cette fonctionnalité a été précédemment introduite par un fichiergatsby-browser.jset a été déplacée vers un composant importé dynamiquement . Par conséquent, ce code ne s'exécuterait que dans un navigateur. (Nous avons utilisé le package npm à défilement lisse et lors de son importation, il a besoin d'un objetwindow.) Ce problème peut être le coupable, mais je ne sais tout simplement pas comment il est géré dans Next.js.

Gatsby est plus pratique du point de vue du développeur


Finalement, j'ai décidé d'opter pour la version Gatsby du projet. De plus, ici, je n'ai pas pris en compte le très petit avantage de performance que Gatsby a montré par rapport au mécanisme SSG Next.js (je ne me cramponnerai pas sérieusement à l'avantage de 0,6 seconde?). Le fait est que dans la version Gatsby du projet, de nombreuses fonctionnalités PWA sont déjà implémentées, et je n'ai pas vu l'intérêt de les implémenter à nouveau dans la version Next.js de l'application.

Lorsque je venais de créer la première version Gatsby du projet, j'ai pu ajouter rapidement quelques fonctionnalités PWA utiles au projet. Par exemple, pour ajouter à chaque page mes propres balises META nécessaires au référencement, je n'ai eu qu'à lire le manuel . Pour équiper le projet d'un manifeste PWA, je n'avais besoin que d'utiliser le plugin approprié. Afin d'équiper le projet de favicons qui prendraient en charge toutes les plates-formes disponibles (et dans ce cas, il y a toujours un terrible gâchis ), je n'ai même pas eu à faire quoi que ce soit, car le support favicon fait partie du plugin responsable du manifeste. C'est très confortable!

L'implémentation des mêmes fonctionnalités dans la version Next.js de l'application nécessiterait plus de travail. Il faudrait que je cherche des manuels de formation, toutes sortes de «bonnes pratiques». Et le fait que je réussirais, de toute façon, ne me donnerait aucun avantage. Après tout, néanmoins, la version Next.js du projet ne diffère pas en performances supérieures à sa version Gatsby. C'est d'ailleurs la raison pour laquelle j'ai décidé de simplement désactiver les fonctionnalités correspondantes de la version Gatsby du projet, en la comparant à la version Next.js. La documentation Next.js est plus concise que la documentation Gatsby (peut-être le fait est que Next.js est plus petit que Gatsby) J'aime beaucoup le tutoriel gamified Next.js. Mais la documentation Gatsby plus complète est plus précieuse avec le développement réel de PWA, même si à première vue elle semble énorme.


Documentation Gatsby

Vrai, je ne peux pas me taire sur les points forts de Next.js:

  • Grâce au tutoriel et à la documentation concise de Next.js, il semble que ce framework puisse être appris plus rapidement que Gatsby.
  • Le système de chargement de données utilisé dans Next.js est basé sur des fonctions asynchrones et l'API Fetch. Par conséquent, lors du développement de Next.js, le développeur n'a pas le sentiment qu'il a besoin d'apprendre GraphQL afin de tirer pleinement parti des capacités du framework.
  • Next.js TypeScript, Gatsby , ( ). Next.js , , , .

Grâce au fait que Next.js a amélioré le support SSG, ce framework est devenu un outil puissant qui permet, au niveau de chaque page individuelle, de choisir la méthode de travail avec. Il peut s'agir de SSR, SSG ou CSR.

En fait, si je pouvais générer cette application sous une forme complètement statique, Next.js me conviendrait mieux, car je pourrais utiliser l'Algolia JS-API standard et conserver le code de chargement des données dans le même fichier que et le code du composant. Étant donné qu'Algolia n'a pas d'API GraphQL intégrée et qu'il n'y a pas de plugin Gatsby pour Algolia, l'implémentation d'un tel mécanisme dans Gatsby nécessiterait l'ajout de ce code à un nouveau fichier . Et cela va à l'encontre de la manière déclarative intuitive de décrire les pages.

À propos des moyens supplémentaires d'améliorer les performances du projet


Après avoir résolu le problème du choix d'un cadre, il peut être noté qu'il existe d'autres moyens d'améliorer les performances du projet qui ne sont pas liés au cadre. Ces améliorations pourraient bien porter la cote du projet Lighthouse à 100.

  • Dans la liste de diffusion de mars Algolia , il a été recommandé d'ajouter un indice preconnectpour augmenter encore la vitesse d'exécution des requêtes. (Vrai, malheureusement, le mauvais fragment de code est donné dans la newsletter. Voici le bon code.)
  • . JS- CSS-, webpack- Gatsby. Gatsby . , , Netlify Amazon S3. , Firebase Hosting, , .
  • Nous utilisons des images JPEG et PNG téléchargées par les créateurs de startups dans l'application. Nous ne les compressons pas et ne les optimisons pas. L'amélioration de cet aspect de notre application est tout un défi et dépasse la portée de ce projet. De plus, ce serait génial si toutes ces images étaient converties au format WebP. Par conséquent, nous devions stocker des images en utilisant un seul format graphique très efficace. Malheureusement, comme pour de nombreuses autres fonctionnalités PWA, l'équipe de développement de Safari WebKit crée une dépendance avec le support WebP. Maintenant, c'est le seul navigateur majeur qui ne prend pas en charge ce format.

Sommaire


Si nous résumons brièvement ce dont nous parlions ici, nous pouvons dire ce qui suit:

  • La sortie de la version "squelettique" de la page pendant le chargement des données par le client crée à l'utilisateur un sentiment de fonctionnement plus rapide du site Web que lorsque l'utilisateur regarde une page vierge pendant que le serveur charge les données.
  • La version gatsby du site n'était que légèrement plus rapide que la version Next.js. Cependant, le système de plug-in Gatsby et la documentation de projet de haute qualité augmentent la convivialité de ce cadre pour le développeur.

Chers lecteurs! Utilisez-vous des générateurs de sites statiques ou des systèmes de rendu côté serveur pour accélérer vos projets?


All Articles