Gatsby, le cycle de vie

Gatsby est un framework basé sur React. Cette surcouche est loin d’être inutile. En étudiant son cycle de vie, on comprend pourquoi.

Vue d’ensemble

Dans un projet propulsé par Gatsby, on retrouve à la racine le dossier node_modules c’est ce qui va contenir tous les modules et autres packages qu’on aura ajoutés au projet via le fichier package.json.

Le dossier /.cache/ contient tous les fichiers de cache. Il est utilisé par la cache API qui permet de conserver des éléments entre chaque build. Très utile quand on interroge des APIs pour récupérer des données (ex: WordPress), quand on applique des transformations sur des images, etc.

Le dossier public/ contient l’export, autrement dit le résultat du build que l’on génère grâce à Gatsby.

Le dossier src/ contient tout le code qui sert à générer le site, les templates, les composants, le(s) thème(s).

Dans ce même dossier src/ on retrouve des dossiers particuliers comme le dossier src/components/ et le dossier src/pages/. Le premier sert à regrouper tous les composants de votre application et le second sert à créer des pages, ainsi tout fichier javaScript à l’intérieur de ce dernier créera une page sur vote site automatiquement. Ainsi src/pages/contact.js ajoute et contrôle la page /contact.

Si vous faites un blog avec des fichiers markdown il faut les mettre dans src/posts/ mais pour moi ce n’est pas l’usage le plus funky. Gatsby ayant la capacité de se nourrir de n’importe quelle API provider ou presque je préfère aller chercher le contenu ailleurs, exemple au hasard WordPress 🙂

Enfin, on trouve ce que j’appelle des fichiers coeur situés, eux-aussi, à la racine du projet. Il est nécessaire de bien connaître car ils permettent de se « hooker » sur Gatsby pour modifier des comportements.

  • gatsby-config.js
  • gatsby-node.js
  • gatsby-ssr.js
  • gatsby-browser.js

Les fichiers coeur en détails

Allez je vous propose de descendre voir ce qui se passe dans ces fichiers.

gatsby-config.js

C’est sans surprise le fichier de configuration principal du projet. On y ajoute notamment :

  • la liste des plugins (packages NPM, plugins locaux, etc)
  • les métadonnées du site (objet siteMetadata)
  • les mappings
gatsby develop

(idem avec gatsby build). Si une erreur y est présente par exemple, elle s’affichera très tôt dans le terminal et le processus s’arrêtera vite.

Source

gatsby-node.js

Pour fonctionner, Gatsby lance un processus Node, c’est ce qui lui permet, via Webpack, de lancer un serveur à la volée du type http://localhost:8000/

Source

C’est le fichier à modifier si vous devez rajouter des urls dynamiquement en chargeant un template personnalisé par exemple. Vous pourrez y combiner requêtes graphQL et création de page.

L’ API CreatePages intervient après le chargement plugins, du cache, le bootstrap, la construction du schema graphQL. Cela permet d’accéder à toutes ces briques pour pouvoir rajouter nos propres noeuds. 

L’ exemple typique est de faire des requêtes avec graphQL, récupérer les données et les déverser dans un template associé :

const path = require(`path`);
const slash = require(`slash`);

exports.createPages = async ({ graphql, actions }) => {
  const { createPage } = actions;
  const result = await graphql(`
    {
      allWordPressPage {
        edges {
          node {
            slug
          }
        }
      }
    }
`);

 if (result.errors) {
    throw new Error(result.errors)
 }

const pageTemplate = path.resolve(`./src/templates/page.js`);
  result.data.allWordPressPage.edges.forEach(edge => {
    createPage({
      path: `/${edge.node.slug}/`,
      component: slash(pageTemplate),
    })
  });

Source

On associe à toutes les pages créées dans WordPress un template Gatsby unique et les urls sont générées automatiquement grâce aux slugs des pages WordPress.

gatsby-ssr.js

SSR pour server side rendering, mais attention ici c’est à la sauce Gatsby au goût légèrement relevé ^^.

En fait, ce qu’ils appellent SSR ici est la phase qui « devrait » être gérée par un serveur Node mais que Gatsby génère en avance : transformer les assets et les pages écrites en React en HTML que le navigateur peut lire.

Au moment du déploiement, tout est déjà compilé. Pas besoin de serveur Node, l’application tourne directement dans le navigateur.

Hooks à utiliser

Pour une liste complète des APIs SSR : voir ce lien.

En les utilisant, on s’aperçoit que Gatsby nous fournit des données bien utiles telle que le path de la page en cours, la liste des composants déjà présents ou encore des objets nous permettant de wrapper nos codes afin que ceux-ci s’ajoutent au bon endroit et au bon moment.

Bonnes pratiques avec un exemple

Dans cache/default-html.js on trouve le html.js par défaut utilisé par Gastby.

Il est possible de copier ce fichier, de le placer dans le dossier src de Gatsby et de le renommer en html.js :

cp .cache/default-html.js src/html.js

Dans ce fichier on aura certaines ancres sur lesquelles se basent les APIs précédemment citées :

   {props.headComponents}
      </head>

Plusieurs choses à ce sujet :

  • ces ancres ne doivent pas être supprimées sinon Gatsby et les plugins ne vont plus fonctionner ou alors mal
  • si ce fichier coeur évolue (ce qui est probable) la mise à jour pourra être complexe
  • tout ce qui est inséré dans ce fichier n’est pas rendu « live » comme les autres composants

Je pourrais continuer la liste mais vous voyez l’idée. C’est une mauvaise pratique que de le modifier à la main comme ça.

Une méthode bien meilleure est d’utiliser les APIs.

Par exemple, pour ajouter une simple classe HTML à la balise body de la page, on va éviter d’écraser le fichier html.js dans votre projet, on éditera le fichier gatsby-ssr.js et on passera plutôt par l’API onRenderBody :

export const onRenderBody = ({setBodyAttributes}) => {
    setBodyAttributes({className: 'mon-site'})
};

Source

Ajouter un script dans le body

Si à côté de cela, on veut ajouter un appel de script au plus près de l’ouverture de la balise body par exemple, on pourra faire :

import React from "react"

export const onRenderBody = ({setPreBodyComponents}) => {
    setPreBodyComponents([<script key="lib-u" src="https://cdn.uudar.lil/lib/latest/ultimate_lib.js"/>]);
};

gatsby-browser.js

Typiquement le fichier à utiliser pour paramétrer les services workers et globalement tout ce qui intervient une fois que l’application a fini de charger et tout ce qui doit affecter le navigateur.

Un code que j’utilise souvent :

onServiceWorkerUpdateReady = () => window.location.reload();

De cette façon je force l’update du site automatiquement quand il y a des changements sans avoir besoin d’attendre que l’internaute navigue.

Dans un autre registre, le hook onClientEntry peut servir à ajouter le code Google Analytics.

Pour une liste complète des APIs Browser, voir ce lien.

Runtime !== Build time

Allez on remonte pour retourner à une vision d’ensemble et essayer de comprendre le déroulé global.

Le runtime

C’est celui qu’on lance via la commande gatsby develop.

La description suivante est basée sur cette source :

  1. Gatsby commence par regarder le fichier gatsby-config.js avec la liste des plugins à charger
  2. ensuite il regarde dans le dossier ./cache pour chopper les éléments qui y seraient présents
  3. puis il met les données pré-chargées dans un schéma graphQL
  4. puis il ajoute les pages dans la mémoire, depuis createPages dans gatsby-node.js mais également depuis tous les codes qui utilisent l’API (plugins notamment) ou les éventuelles pages rajoutées dans src/pages/
  5. il joue ensuite toutes les requêtes graphQL et statiques
  6. enfin il écrit les pages dans le cache

Le build time

Le build time c’est celui qu’on lance avec gatsby build.

Il constitue la toute dernière phase avant déploiement. Il va générer le site final qui partira dans la prochaine mise en prod.

Contrairement au runtime, pas de « hot reload » (~ la prise en compte live des changement dans le code très pratique lors du développement), ce n’est plus le moment de travailler ^^. Tout changement impliquerait un nouveau build pour le voir pris en compte.

Le processus n’est pas vraiment le même qu’au runtime. Certaines étapes sont similaires mais le build va aussi supprimer les anciens builds, générer les fichiers statiques dans le dossier public/ ou encore créer les bundles js et css.

Pour contrôler l’aspect du site avant déploiement, on peut lancer :

gatsby serve

Conclusion

Il y a 3 grandes étapes dans ce cycle Gatsby :

  1. Le runtime (develop)
  2. Le build time (build)
  3. Le browser (déploiement et vie)

La présence de très nombreux hooks dans les APIs au cours de ces 3 phases est une énorme abstraction sur React dont les développeur(se)s peuvent se saisir pour créer leurs applications.

C’est la clé pour comprendre le succès passé et à venir de l’écosystème Gatsby. Avec ce cycle en tête, la route des plugins nous attend…