Fermer

juin 29, 2021

Décomposer les constructions volumineuses avec Netlify et Next.js —


À propos de l'auteur

Átila Fassina a pour mission de simplifier le code. Lorsqu'il n'enregistre pas de screencasts ou de cours, vous pouvez le trouver en train d'écrire et de parler de jamstack, …

En savoir plus sur

tila

La génération statique est idéale pour les performances, mais pas avant que l'application ne soit trop volumineuse et que les temps de construction atteignent le plafond. Aujourd'hui, nous verrons comment les nouveaux constructeurs à la demande de Netlify peuvent résoudre ce problème. De plus, nous l'associons à la régénération statique incrémentielle de Next.js pour la meilleure expérience utilisateur et développeur. Et, bien sûr, comparez ces résultats !

L'une des plus grandes difficultés à travailler avec des sites Web générés de manière statique est la lenteur croissante des versions à mesure que votre application se développe. Il s'agit d'un problème inévitable auquel toute pile est confrontée à un moment donné et il peut survenir à partir de différents points en fonction du type de produit avec lequel vous travaillez.

Par exemple, si votre application comporte plusieurs pages (vues, itinéraires) lors de la génération du déploiement. artefact, chacune de ces routes devient un fichier. Ensuite, une fois que vous avez atteint des milliers, vous commencez à vous demander quand vous pouvez déployer sans avoir à planifier à l'avance. Ce scénario est courant sur les plateformes de commerce électronique ou les blogs, qui représentent déjà une grande partie du Web mais pas la totalité. Cependant, les routes ne sont pas le seul goulot d'étranglement possible.

Une application gourmande en ressources finira également par atteindre ce tournant. De nombreux générateurs statiques effectuent une optimisation des actifs pour assurer la meilleure expérience utilisateur. Sans optimisations de build (builds incrémentiels, mise en cache, nous y arriverons bientôt), cela deviendra également ingérable – pensez à parcourir toutes les images d'un site Web : redimensionner, supprimer et/ou créer de nouveaux fichiers encore et encore. Et une fois tout cela fait : rappelez-vous que Jamstack sert nos applications depuis les bords du Content Delivery Network. Nous devons donc toujours déplacer les éléments du serveur sur lequel ils ont été compilés vers les bords du réseau.

Architecture de service général Jamstack

Architecture de service général Jamstack ( Grand aperçu)

En plus de tout cela, il y a aussi un autre fait : les données sont souvent dynamiques, ce qui signifie que lorsque nous construisons notre application et la déployons, cela peut prendre quelques secondes, quelques minutes, voire une heure. Pendant ce temps, le monde continue de tourner, et si nous récupérons des données d'ailleurs, notre application est vouée à devenir obsolète. Inacceptable ! Construire à nouveau pour mettre à jour !

Créer une fois, mettre à jour si nécessaire

Résoudre les Bulky Builds a été une priorité pour pratiquement toutes les plates-formes, frameworks ou services Jamstack pendant un certain temps. De nombreuses solutions tournent autour de builds incrémentiels. En pratique, cela signifie que les builds seront aussi volumineux que les différences qu'ils comportent par rapport au déploiement actuel.

La définition d'un algorithme diff n'est cependant pas une tâche facile. Pour que l'utilisateur final bénéficie réellement de cette amélioration, il existe des stratégies d'invalidation du cache qui doivent être envisagées. Pour faire court : nous ne voulons pas invalider le cache pour une page ou un élément qui n'a pas changé.

Next.js a proposé la régénération statique incrémentielle (ISR). Essentiellement, c'est un moyen de déclarer pour chaque route à quelle fréquence nous voulons qu'elle soit reconstruite. Sous le capot, cela simplifie une grande partie du travail côté serveur. Parce que chaque route (dynamique ou non) se reconstruira en fonction d'un laps de temps spécifique, et elle s'intègre parfaitement dans l'axiome Jamstack d'invalider le cache à chaque construction. Considérez-le comme l'en-tête max-age mais pour les routes de votre application Next.js.

Pour démarrer votre application, ISR n'est qu'à une propriété de configuration. Sur votre composant de route (à l'intérieur du répertoire /pages), accédez à votre méthode getStaticProps et ajoutez la clé revalidate à l'objet de retour :

export async function getStaticProps() {
  const { limit, count, pokemons } = wait fetchPokemonList()
  
  revenir {
    accessoires : {
      limite,
      compter,
      pokémons,
    },
    revalider : 3600 // secondes
  }
}

L'extrait ci-dessus garantira que ma page se reconstruit toutes les heures et récupère plus de Pokémon à afficher.

Nous recevons toujours les builds en vrac de temps en temps (lors de l'émission d'un nouveau déploiement). Mais cela nous permet de dissocier le contenu du code, en déplaçant le contenu vers un Système de gestion de contenu (CMS), nous pouvons mettre à jour les informations en quelques secondes, quelle que soit la taille de notre application. Adieu les webhooks pour la mise à jour des fautes de frappe !

On-Demand Builders

Netlify a récemment lancé On-Demand Builders qui est leur approche pour prendre en charge ISR pour Next.js, mais fonctionne également avec des frameworks tels que Eleventy et Nuxt. Lors de la session précédente, nous avons établi que l'ISR était un grand pas vers des temps de construction plus courts et a traité une partie importante des cas d'utilisation. Néanmoins, les mises en garde étaient là :

  1. Des builds complets sur un déploiement continu.
    L'étape incrémentielle ne se produit qu'après le déploiement et pour les données. Il n'est pas possible d'envoyer du code de manière incrémentielle.
  2. Les versions incrémentielles sont un produit du temps.
    Le cache est invalidé en fonction du temps. Des builds inutiles peuvent donc se produire, ou les mises à jour nécessaires peuvent prendre plus de temps en fonction de la période de revalidation définie dans le code.

La nouvelle infrastructure de déploiement de Netlify permet aux développeurs de créer une logique pour déterminer quels éléments de leur application seront construits sur le déploiement et quels éléments seront différé (et comment ils seront différés).

  • Critique
    Aucune action n'est nécessaire. Tout ce que vous déployez sera construit sur push.
  • Deferred
    Un élément spécifique de l'application ne sera pas construit lors du déploiement, il sera différé pour être construit à la demande chaque fois que la première demande se produit , alors il sera mis en cache comme toute autre ressource de son type.

Création d'un générateur à la demande

Tout d'abord, ajoutez un package netlify/functions en tant que devDependency à votre projet :

yarn add -D @netlify/functions

Une fois cela fait, cela revient à créer une nouvelle Fonction Netlify. Si vous n'avez pas défini de répertoire spécifique pour eux, rendez-vous sur netlify/functions/ et créez un fichier de n'importe quel nom pour votre constructeur.

import type { Handler } depuis '@netlify/functions'
importer { builder } depuis '@netlify/functions'

const myHandler : Handler = async (événement, contexte) => {
  revenir {
    code d'état : 200,
    body : JSON.stringify({ message : 'Construit à la demande ! 🎉' }),
  }
}
export const handler = builder(myHandler)

Comme vous pouvez le voir dans l'extrait ci-dessus, le générateur à la demande se sépare d'une fonction Netlify normale car il encapsule son gestionnaire dans une méthode builder(). Cette méthode connecte notre fonction aux tâches de construction. Et c'est tout ce dont vous avez besoin pour qu'une partie de votre application soit différée pour la construction uniquement lorsque cela est nécessaire. Petites versions incrémentielles dès le départ !

Next.js sur Netlify

Pour créer une application Next.js sur Netlify, il existe 2 plugins importants que l'on doit ajouter pour avoir un meilleure expérience en général : Netlify Plugin Cache Next.js et Essential Next-on-Netlify. Le premier met en cache votre NextJS plus efficacement et vous devez l'ajouter vous-même, tandis que le second apporte quelques légers ajustements à la façon dont l'architecture Next.js est construite afin qu'elle s'adapte mieux à celle de Netlify et soit disponible par défaut pour chaque nouveau projet que Netlify peut identifier est en utilisant Next.js.

Générateurs à la demande avec Next.js

Performances de construction, performances de déploiement, mise en cache, expérience de développeur. Ce sont tous des sujets très importants, mais c'est beaucoup – et il faut du temps pour s'installer correctement. Ensuite, nous arrivons à cette vieille discussion sur la concentration sur l'expérience du développeur au lieu de l'expérience utilisateur. C'est le moment où les choses vont à un endroit caché dans un arriéré pour être oubliées. Pas vraiment.

Netlify vous soutient. En quelques étapes seulement, nous pouvons exploiter toute la puissance de Jamstack dans notre application Next.js. Il est temps de retrousser nos manches et de tout assembler maintenant.

Définition des chemins pré-rendus

Si vous avez déjà travaillé avec la génération statique dans Next.js, vous avez probablement entendu parler de getStaticPaths méthode. Cette méthode est destinée aux routes dynamiques (modèles de page qui rendront un large éventail de pages).
Sans trop s'attarder sur les subtilités de cette méthode, il est important de noter que le type de retour est un objet avec 2 clés, comme dans notre Proof-of-Concept ce sera le fichier [Pokémon]dynamic route :

 exporter la fonction asynchrone getStaticPaths() {
  revenir {
    chemins : [],
    repli : 'bloquant',
  }
}
  • paths est un array effectuant tous les chemins correspondant à cette route qui sera pré-rendue
  • fallback a 3 valeurs possibles : blocking, trueou false

Dans notre cas, notre getStaticPaths est déterminant :

  1. Aucun chemin ne sera pré-rendu ;
  2. Chaque fois que cette route est appelée, nous ne servirons pas de modèle de secours, nous rendrons la page à la demande et ferons attendre l'utilisateur, bloquant l'application de faire autre chose.

Lors de l'utilisation à la demande Constructeurs, assurez-vous que votre stratégie de secours répond aux objectifs de votre application, les documents officiels Next.js : fallback sont très utiles.

Avant les constructeurs à la demande, notre getStaticPaths était légèrement différent :

exporter la fonction asynchrone getStaticPaths() {
  const { pokémons } = attendre fetchPkmList()
  revenir {
    chemins : pokemons.map(({ nom }) => ({ params : { pokemon : nom } })),
    repli : faux,
  }
}

Nous rassemblions une liste de toutes les pages pokémon que nous avions l'intention d'avoir, mappions tous les objets pokemon à une chaîne avec le nom pokémon, et renvoyions le { params } objet le transportant vers getStaticProps. Notre fallback a été défini sur false car si une route ne correspondait pas, nous voulions que Next.js lance une page 404 : Not Found.

Vous pouvez vérifier les deux versions déployées sur Netlify :

Le code est également open source sur Github et vous pouvez facilement le déployer vous-même pour vérifier les temps de construction. Et avec cette file d'attente, nous passons à notre sujet suivant.

Build Times

Comme mentionné ci-dessus, la démo précédente est en fait une Proof-of-Conceptrien n'est vraiment bon ou mauvais si nous ne peut pas mesurer. Pour notre petite étude, je suis passé à la PokéAPI et j'ai décidé d'attraper tous les pokémons.

Pour des raisons de reproductibilité, j'ai plafonné notre demande (à 1000). Ce ne sont pas vraiment tous dans l'API, mais cela impose que le nombre de pages soit le même pour toutes les versions, peu importe si les choses sont mises à jour à tout moment.

export const fetchPkmList = async () => {
  const resp = wait fetch(`${API}pokemon?limit=${LIMIT}`)
  const {
    compter,
    résultats,
  } : {
    compte : nombre
    résultats: {
      nom : chaîne
      URL : chaîne
    }[]
  } = attend resp.json()
  revenir {
    compter,
    pokémons : résultats,
    limite : LIMITE,
  }
}

Et puis a déclenché les deux versions dans des branches séparées vers Netlify, grâce aux déploiements de prévisualisation, elles peuvent coexister dans pratiquement le même environnement. Pour vraiment évaluer la différence entre les deux méthodes, l'approche ODB était extrême, aucune page n'a été pré-rendue pour cette route dynamique. Bien qu'il ne soit pas recommandé pour les scénarios du monde réel (vous souhaiterez pré-rendre vos itinéraires à fort trafic), il marque clairement la plage d'amélioration des performances de temps de construction que nous pouvons atteindre avec cette approche.

StratégieNombre de PagesNombre d'actifsTemps de créationTemps de déploiement total
Entièrement statique généré100210052 minutes 32 secondes4 minutes 15 secondes
Activé -Demand Builders2052 seconds52 seconds

Les pages de notre petite application PokéDex sont assez petites, les actifs d'image sont très maigres, mais les gains sur le temps de déploiement sont très important. Si une application a un nombre moyen à élevé de routes, cela vaut vraiment la peine d'envisager la stratégie ODB.

Cela rend vos déploiements plus rapides et donc plus fiables. L'atteinte des performances ne se produit que lors de la toute première demande, à partir de la demande suivante et au-delà, la page rendue sera mise en cache directement sur Edge, ce qui rendra les performances exactement les mêmes que celles générées entièrement statiques.

L'avenir : rendu persistant distribué

]Le même jour, les constructeurs à la demande ont été annoncés et mis en accès anticipé, Netlify a également publié leur Request for Comments on Distributed Persistent Rendering (DPR).

DPR is the next step for On -Constructeurs de la demande. Il capitalise sur des versions plus rapides en utilisant ces étapes de construction asynchrones, puis en mettant en cache les actifs jusqu'à ce qu'ils soient réellement mis à jour. Plus de versions complètes pour un site Web de 10 000 pages. DPR permet aux développeurs d'avoir un contrôle total sur la construction et le déploiement des systèmes grâce à une mise en cache solide et à l'utilisation de générateurs à la demande.

Imaginez ce scénario : un site Web de commerce électronique contient 10 000 pages de produits, cela signifie que cela prendrait environ 2 heures. pour construire l'intégralité de l'application pour le déploiement. Nous n'avons pas besoin de discuter à quel point cela est pénible.

Avec DPR, nous pouvons définir les 500 premières pages à construire sur chaque déploiement. Nos pages les plus fréquentées sont toujours prêtes pour nos utilisateurs. Mais nous sommes une boutique, c'est-à-dire que chaque seconde compte. Ainsi, pour les 9 500 autres pages, nous pouvons définir un hook post-build pour déclencher leurs constructeurs – déployant le reste de nos pages de manière asynchrone et immédiatement en cache. Aucun utilisateur n'a été blessé, notre site Web a été mis à jour avec la version la plus rapide possible, et tout le reste qui n'existait pas dans le cache a ensuite été stocké.

Conclusion

Bien que de nombreux points de discussion dans cet article soient conceptuels et que la mise en œuvre soit à définir, je suis enthousiasmé par l'avenir du Jamstack. Les progrès que nous réalisons en tant que communauté tournent autour de l'expérience de l'utilisateur final.

Que pensez-vous du rendu persistant distribué ? Avez-vous essayé les générateurs à la demande dans votre application ? Faites-moi savoir plus dans les commentaires ou appelez-moi sur Twitter. Je suis vraiment curieux !

Références

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




Source link