Article écrit par Gaëtan D.
À la découverte des Nuxt Server Components (beta)
Si vous avez récemment travaillé sur un projet React, vous avez surement entendu parler des server components.
Il s’agit d’un nouveau type de rendu de composants, initié par la communauté React, notamment via le framework Next.
Et bien bonne nouvelle, depuis la version 3.9, Nuxt a sa propre implémentation des server components,les « Nuxt Islands » !
L’intérêt de ces composants est leur capacité de rendu uniquement côté serveur, ce qui élimine le besoin dit d’hydratation.
Soulevons le capot !
Qu’est ce que l’hydratation ?
Avant de détailler les gains de performance qu’ils peuvent nous apporter et comment les utiliser avec Nuxt, penchons-nous sur cette notion énigmatique d’« hydratation ».
Présent dans les applications Server-Side Rendered (ou Statically Rendered), c’est un processus qui permet de rendre interactif le HTML statique.
Vous avez probablement remarqué que parfois, lorsque vous souhaitez interagir avec un élément d’une application Nuxt juste après qu’elle soit affichée à l’écran, rien ne se passe…
En arrivant dans le navigateur, il ne s’agit pas encore d’une application interactive, mais d’un simple fichier HTML statique !
Ensuite, le navigateur détecte les balises <script>
, télécharge puis exécute le code qui transforme les éléments HTML statiques en instances de composants Vue montées sur les nœuds DOM déjà existants.
Dans la plupart des cas, ce code fait partie du bundle principal à moins qu’il soit explicitement divisé dans un fichier séparé.
Les limites de l’hydradation
Le souci est que l’hydratation est un processus gourmand qui va malheureusement être exécuté sur tous vos composants.
Chacun d’entre eux apporte davantage de code JavaScript à votre bundle. Plus il y a de code JavaScript à exécuter, plus le processus d’hydratation est long, et plus il est probable que les utilisateurs cliqueront sur un élément dont l’interactivité sera retardée.
S’il n’est pas un problème majeur sur les ordinateurs modernes avec un bonne connexion, ce décalage peut devenir très frustrant en mobilité.
En plus de l’inconfort, cela favorise aussi le sentiment trompeur d’obsolescence de nos terminaux.
Prioriser les composants à hydrater
Après analyse, on se rend rapidement compte que certains composants de nos pages n’ont pas besoin d’être hydratés prioritairement, ni parfois d’être hydratés tout court !
Dans le premier cas, la solution sera de reprogrammer l’hydratation à un moment plus pertinent.
Rien n’est disponible à ce jour dans Nuxt pour ça mais cela est très facile à mettre en place avec le module nuxt-lazy-hydrate par exemple.
Dans le second cas, nous pouvons nous appuyer sur nos nouveaux amis les server components !
Passons à la pratique.
Depuis la version 3.9 de Nuxt, cette fonctionnalité est expérimentale, mais fera partie de la prochaine version majeure annoncée pour ce dernier trimestre 2024.
Pour l’utiliser dès aujourd’hui, rien de compliqué, il suffit de l’activer dans la configuration.
// nuxt.config.js
export default defineNuxtConfig({
experimental: {
componentIslands: true
}
})
Vous pourrez alors convertir un composant en server component en utilisant l’extension .server.vue
.
(Notez qu’il existe la même chose pour un rendu uniquement coté client avec .client.vue
.)
<!-- components/Character.server.vue -->
<template>
<div>
<img :src="character.image" :alt="character.name">
<div>
<h2>{{ character.name }}</h2>
<p v-if="character.dateOfBirth">
Date de naissance: {{ format(parse(character.dateOfBirth, 'dd-MM-yyyy', new Date()), 'dd/MM/yyyy')}}
</p>
</div>
</div>
</template>
<script setup>
import { format, parse } from 'date-fns';
const props = defineProps({
character: {
type: Object,
required: true
}
});
</script>
Grâce à cette technique, la page se chargera plus rapidement, sans étape d’hydratation supplémentaire côté client.
Dans l’exemple ci-dessus, la librairie date-fns
ne fera pas partie du bundle que le navigateur devra télécharger !
Sous le capot, les server components s’appuient sur le composant NuxtIsland
fourni par le framework.
Celui-ci peut également être utilisé par nos soins pour déterminer selon le contexte si un composant doit être considéré comme un server component ou non.
<!-- pages/characters.vue -->
<template>
<div>
<h1>Harry Potter Characters</h1>
<div>
<NuxtIsland v-for="c in characters" :key="c.id" name="Character" :props="{character: c}" />
</div>
</div>
</template>
<script setup>
const characters = [{
"id": 1,
"name": "Harry Potter",
"image": "https://ik.imagekit.io/hpapi/harry.jpg",
"dateOfBirth": "31-07-1980"
},{
"id": 2,
"name": "Hermione Granger",
"image": "https://ik.imagekit.io/hpapi/hermione.jpeg",
"dateOfBirth": "19-09-1979"
}];
</script>
Cerise sur le gâteau, il reste possible d’ajouter de l’interactivité utilisant un slot au sein d’un server component en activant une fonctionnalité expérimentale complémentaire : selectiveClient
.
// nuxt.config.js
export default defineNuxtConfig({
experimental: {
componentIslands: true,
selectiveClient: true
}
})
<!-- components/Character.server.vue -->
<template>
<div>
<img :src="character.image" :alt="character.name">
<div>
<h2>{{ character.name }}</h2>
<p v-if="character.dateOfBirth">
Date de naissance: {{ format(parse(character.dateOfBirth, 'dd-MM-yyyy', new Date()), 'dd/MM/yyyy')}}
</p>
<slot />
</div>
</div>
</template>
<script setup>
// ...
</script>
<!-- pages/characters/[id].vue -->
<template>
<Character :character="character">
<ComponentWithInteractivity>
</Character>
</template>
<script>
//...
</script>
Au delà du gain potentiel de performance dans le cas de composants incluant des opérations complexes ou coûteuses, l’utilisation des server components apportent d’autres avantages :
- Sécurité : Lorsque votre logique nécessite l’accès à une base de données ou a besoin d’une clé privée ou d’un secret, les server components peuvent être une solution utile.
- Interchangeable : Ils peuvent prendre en charge toutes les caractéristiques des composants normaux, y compris l’état partagé, l’accès au
router
, etc. On peut donc facilement passer d’un type de composant à un autre.
Pour finir, si vous souhaitez garder un oeil sur la roadmap de cette prometteuse fonctionnalité, un ticket a été ouvert pour suivre son évolution.
Bonne expérimentation !