Blog des Gens Compliqués

J'ai testé Nuxt et Vue 3 après plus d'un an sans toucher à un framework JS

16/07/2025 21:24:26+02:00|Par DkVZ
11 minutes de lecture (facile)

Aaah le JavaScript, mon vieux copain.

Je l'utilise toujours occasionnellement pour des tâches qui n'ont rien à voir avec le web.

Nettement plus rarement pour créer des API depuis que j'ai découvert Go mais j'ai encore quelques vieux services qui tournent en Node sans jamais poser de problèmes.

A côté de ça j'ai surtout pas mal de scritps d'admin système en tout genre écrits en JS. Vous trouvez ça étrange? D'accord. Mais pourquoi?

Je sais que Perl traîne toujours dans le fond du tirroir à côté de ma jeunesse perdue. Mais bon, qui a envie d'écrire du Perl en 2025? Qui sait ce que c'est le Perl en 2025?

une partie du code Perl de Asciiquarium qui montre des poissons en ASCII
Le fameux asciiquarium que tout le monde connaît est un script PERL

En plus il s'est passé quelques trucs chouettes dans les dernières versions de Node, par exemple on peut utiliser await comme ça tout de suite dans le script principal, il faut plus créer une vieille fonction async appelée de suite comme on faisait avant:

#!/bin/env node

(async () => {
  const machin = await chopperUnTrucCool()
  console.log(machin)
})()

Non maintenant on peut juste l'écrire tout de suite dans le script, et en plus fetch fonctionne aussi de base, il faut même pas l'importer ni bien sûr installer node-fetch ou ce bon vieux Axios qui jouit toujours d'une grande popularité.

Exemple de script qui réalise une requête web avec désérialisation, en quelques lignes, avec des await et sans devoir importer quoi que ce soit:

#!/bin/env node

const resp = await fetch("https://api.dkvz.eu/last-comment")
const body = await resp.json()
console.log(body)

Propre. Je suis désolé mais c'est juste mieux que Perl et ses dollars devant toutes les variables. Le rapport avec Nuxt et Vue?

Y en a pas. Mais le temps il passe et il passe vite en JavaScript. Et, parfois, c'est bien.

Bon et Nuxt 3 alors?

J'ai le projet de remplacer mon blog depuis 10 ans.

A l'époque de sa genèse c'était la "mode" des SPA au point que même les Googles eux-mêmes incitaient à les utiliser (pour pousser les nouvelles technos de Chrome et du coup, Chrome).

Mon tout premier blog qui utilisait Polymer (web components créés par Google avant que les web components ne deviennent un vrai standard des naviguateurs (que personne n'utilise lol)) est toujours disponible et fonctionne toujours.

Le vent tournera un peu plus tard pour revenir à ce qu'on faisait en PHP dans les années 2000 (générer du HTML SUR LE SERVEUUUUR).

Certains robots sont juste incapables d'interpréter le JavaScript et certaines choses "basiques" comme changer des tags meta sur une page ou l'autre ben c'est pas possible sur une SPA.

Ce qui est ultra craignos parce que ça veut dire que les intégrations avec les réseaux sociaux sont toutes pourries (y en a pas du tout ou bien c'est toujours la même chose qui apparait (même description, image, etc.)).

Je me suis retrouvé à créer des redirections infâmes ciblant spécifiquement les robots (identifiés par leur User-Agent) vers des versions générées par le serveur de mes articles (exemple).

une intégration bluesky vers un de mes articles où l'image est tellement agrandie qu'elle est fort pixelisée (image de Geforce 3)
Mes intégration sociales actuelles — Non je fais pas exprès que les images soient si dégeulasses

Quoi qu'il en soit, un site ça devrait pouvoir au moins un petit peu fonctionner sans JavaScript. Un scénario où le mien te montre juste un spinner qui tourne pour toujours.

Triste, non?

Bon et Nuxt / Vue 3 alors?

Oui une minute, j'y viens!

Les frameworks JavaScript "hybrides" (génération serveur + HYDRATION (hydratation? Mouillage?) + transformation en SPA) ne sont pas nouveaux.

Je pense que Next JS était (est toujours?) le plus connu. Même Svelte en a un de ces projets, nommé Sapper je crois, mais comme pour Svelte-tout-court tout le monde s'en fout et personne ne l'utilise.

En fait c'est le seul truc qui a pas changé depuis la dernière fois que j'ai touché à du frontend: Svelte a l'air encore plus boudé qu'avant alors que je le trouve toujours vraiment... Clair. Et compréhensible par des néophytes. Mais ouais tout le monde s'en balance.

J'avais écrit un énorme article sur les frameworks JS en 2019 qui doit détenir le prix de l'article le moins populaire de tout le Blog (c'est dire) et qui présente quelques inexactitudes et manquements dans mes critiques de React notamment.

Je suis pas vraiment un grand fan de React et je pense pas le devenir. Une série de useEffect obscurs avec un tableau de dépendances à rallonge et des useMemo partout qui te font questionner le sens de la vie.

Reste plus que Vue... Et sa structure toute foireuse à base de bon gros objet avec une vieille fonction data() et tout un gros tas de propriété qui ressemble presque à ce que j'ai écrit pour la courante (le mot est bien choisi) version de ce blog.

He ben vous savez quoi? On est plus obligés d'écrire le code Vue en mode GROS OBJET, ils ont sorti un truc appelé la composition API.

En gros ça ressemble à un vieux mix Svelte et React, en plus clair.

L'exemple du bon vieux compteur qui est sur leur site:

<script setup lang="ts">
import { ref, onMounted } from 'vue'

// reactive state
const count = ref(0)

// functions that mutate state and trigger updates
const increment = () => {
  count.value++
}

// Lifecycle hooks:
onMounted(() => {
  console.log(`The initial count is ${count.value}.`)
})
</script>

<template>
  <button @click="increment">Count is: {{ count }}</button>
</template>

Les fonctions "cycle de vie" sont assez claires.

Aussi, ça se voit pas dans l'exemple mais pour les gros amateurs de TypeScript (il paraît qu'ils existent parce que les gens s'en plaignent régulièrement) on peut aussi facilement typer (ça se dit typer?) ses props, annoncer quel tel composant va émettre tel événement, ...

Avec Nuxt il faut même pas importer les trucs, t'as presque tout qui s'auto-importe. Et ce qui s'auto-importe pas, tu peux le configurer facilement pour qu'il s'auto-importe quand même.

On voit aussi passer des fonctions useMachin() et useBidule() qui ne sont pas sans rappeler les hooks de React et renvoient générallement une ou plusieurs refs qui correspondent à un comportement particulier.

Par exemple, useSeoMeta permet de modifier pas mal de tags meta et autres qui trouvent leur place dans le tag <head> de la page en cours au niveau génération serveur... Mais aussi par le client pour ce que ça vaut (modifier le titre, par exemple, étant assez important).

Par ailleurs, cette histoire de refs n'a rien à voir avec "référence vers un noeud du DOM" (notion qui existe aussi en Vue et euh... Est aussi appelée Ref...) et correspond plutôt à la notion traditionnelle de state.

Evidemment il s'agit de vaguement ordonner son JavaScript avec les déclarations / refs en premier puis le reste s'aligne un peu naturellement, sans avoir 45 useMemo et autres useCallback qui se suivent plus ou moins en faisant des choses totalement différentes.

Dans tout ça, Nuxt ajoute encore plus de magie et de facilité avec du routage basé sur des fichiers mais tout de même entièrement configurable si besoin, un typage automatique de l'API interne, des imports automatiques, des "modules" qui permettent d'ajouter, par exemple, Tailwind, en une seule ligne de config.

Pour la partie serveur ou backend comme on dit dans le milieu, Il te sort un serveur Node portable assez simple à déployer.

A côté de ça se trouvent d'autres "techniques d'hébergement" comme la génération statique qui fonctionne naturellement de manière récursive et j'avoue être tenté de générer mon blog comme ça (en gardant l'API une fois que l'HYDRATATION a eu lieu) pour éviter d'avoir un vieux process Node peu utile et redondant à laisser trainer sur mon serveur.

Il y a des middlewares partout et une structure relativement rigide au niveau des fichiers, ce que j'apprécie personnellement. Avoir trop de choix ça me fatigue.

Le serveur est basé sur un autre projet: Nitro, qui fournit tout ce qui est API, WebSocket, caching, ... Sans oublier un routage basé sur des fichiers.

Le typage de l'API du projet en cours est automatiquement découvert pour utilisation dans les fichiers .vue de Nuxt.

Enfin, pourvu que ça fonctionne sur votre éditeur. Même sous VS Code il y a plusieurs extensions pour VueJS, il faut bien choisir la bonne — détail tellement important qu'il est dans le README des nouveaux projets Nuxt générés.

J'utilise depuis peu NEOVIM BY THE WAY et j'ai souffert mes premières expériences de mises à jour qui ont cassé des trucs silencieusement dans ma config, m'empêchant d'avoir un environnement fonctionnel pour Nuxt.

Bon, maintenant ça marche après avoir vaguement copié ce que LazyVim utilise en "extra" pour Vue, avec en plus le serveur de langage Tailwind qui est assez chouette (il lag un peu ceci dit).

Un tag template édité dans Neovim avec le menu de suggestions qui montre un apreçu de toutes les couleurs indigo- de Tailwind depuis l'attribut 'class' d'un élément
Je suis vite impressionné mais ces aperçus existent probablement aussi dans VS Code (pas essayé) qui a une extension officielle pour Tailwind et je suis un peu triste d'être passé à côté depuis des années

Après 6h de config j'ai pu enfin me mettre à découvrir comment j'allais esquisser le nouveau Blog.

Y a une (deux?) nouvelles version de Nuxt et Vue qui sont imminentes

J'avais oublié que le monde du JavaScript il te laisse pas le temps de te faire une jarre de café qu'il est déjà 5 versions plus loin et s'approche lentement du cap des 12000 vulnérabilités détectées par npm.

Du coup je vais devoir passer plus de temps à expérimenter en attendant au moins que Nuxt 4 sorte officiellement.

Je suis un peu médisant en vrai parce que Nuxt 3 existe depuis un moment (plus d'un an il me semble).

On va dire que je tombe juste au mauvais moment.

J'ai des problèmes d'hydratation

Qui n'en a pas en ces temps arides?

J'ai jamais vraiment utilisé Next, et j'imagine qu'il a le même genre de considérations. Avoir du code qui peut tourner autant sur le client que sur le serveur de manière bien homogène et propre reste quelque chose de légèrement magique.

J'ai découvert assez rapidement que Nuxt est susceptible à un type d'erreur que je n'avais pas encore vraiment rencontré.

En gros, si le DOM généré côté serveur est différent de celui que le client génère, il te sort qu'il y a de la hydration error et il se passe des choses bizarres pour te punir de ton outrecuidance.

Je l'ai personnellement rencontrée en testant du code de pagination. Oui ben, on passe ses week ends comme on veut hein. Je voyais ça dans ma console:

Console de navigateur affichant un 'hydration mismatch'

Et mon bidule de pagination il affiche qu'il a pas (encore?) reçu l'info sur la dernière page (alors que le serveur l'a eu correctement):

Montre un aperçu d'article et ma pagination avec page 2 sur '?'
Je précise que le futur blog ne va pas ressembler à ça, il est un peu moche mais quand même pas à ce point là

Je détermine la dernière page en récupérant une en-tête envoyée dans la réponse de mon API que je questionne avec useFetch, le principal moyen d'appeler une API depuis Nuxt qui va magiquement fonctionner à la fois pour la génération serveur et pour un appel depuis le client.

En toute innocence j'ai utilisé une ref pour stocker ma dernière page:

const lastPage = ref<number>(0)

Et je la mets à jour une fois que mon useFetch a reçu une réponse, avant ça j'affiche un vieux point d'interrogation en guise de dernière page (on l'a vu plus haut).

He bien, ce point d'interrogation est là pour toujours.

Quelque part ça se tient parce que le client ne réitère pas de requête vers l'API si il a reçu la page déjà générée par le serveur.

Mais alors... Il pourrait aussi recevoir l'info de la dernière page de la part du serveur? Non? Ben non.

Je crois comprendre que les refs ne fonctionnent réellement que côté client (possible que j'explique très mal cette histoire).

Ce que les fonction comme useFetch retournent ne sont pas des refs, ce serait un autre truc qu'ils ont appelé state (bah ouais pourquoi pas, hein).

J'ai donc simplement remplacé ma déclaration de lastPage en ceci:

const lastPage = useState<number>("lastPage", () => 0)

Il faut juste lui donner une "clé" unique en argument utilisée par le cache interne de Nuxt (enfin je crois lol) et l'initialisation se fait avec une fonction (ref utilise simplement une valeur). Me demandez pas pourquoi.

Dans tous les cas ça marche, regardez mon beau paginateur:

pagination avec un chevron pour précédent, suivi de la page courante, d'un slash, de la dernière page (7) et d'un chevron suivant

Ce type d'erreurs semble être une des difficultés à l'usage de Nuxt.

Pour citer un autre exemple, si vous utilisez une date avec les heures-minutes-secondes courantes, le client va générer une date postérieure et vous risquez une erreur d'hydratation.

Ce qui a mené à l'invention d'un composant spécifique <NuxtTime /> dont le but est de mettre d'accord client et serveur sur une date à afficher.

Conclusion

Bon ben j'attends Nuxt 5 puis j'essaye de sortir le nouveau blog.

Je vous dis rendez-vous en 2030, où tout sera finalement généré par une IA.

Je devrais juste faire du fromage et de la bière en fait.

Commentaires

Il faut JavaScript activé pour écrire des commentaires ici

Ajouter un commentaire

Votre commentaire a été ajouté
(enfin, je pense)