Render Vue en terre inconnue

Deux cas d’usage de render functions avec Vue

Nico Prat
nicooprat

--

Nativement, il existe trois solutions pour décrire la vue d’un composant : les templates, la méthode majoritaire et historique parmi les frameworks frontend ; la syntaxe JSX, qui vient du monde de React ; et enfin la dernière dont on va parler ici, la fameuse fonction render des composants. Pour rappel il s’agit de ce genre de code (docs) :

render(h) {
return h('h1', this.blogTitle)
}

Très complète mais forcément très verbeuse, on ne l’utilise que dans des cas très particuliers où la seule manière d’aboutir au résultat attendu est de “tordre” la façon dont est rendu le code du composant. Par exemple, le système de slots dans Vue étant parfois limité (il est impossible dans un template de boucler sur les slots, c’est à dire une sorte de <slot v-for="" />), c’est souvent dans ce cas qu’elle m’est avérée nécessaire. Voici deux cas rencontrés que j’ai trouvé intéressants à développer.

vue-columns : code & démo

Vue-columns

Ma première aventure avec les render functions a été de réaliser une sorte de layout “masonry” (à la Pinterest), c’est à dire en colonnes où chaque élément a sa propre hauteur sans impacter ses copains de la même ligne. Et non, ce n’est toujours pas possible seulement en CSS malheureusement (pour l’instant) !

Après avoir tourné le problème dans tous les sens et écarté les solutions classiques qui agissent sur le DOM après rendu (trop de cas limites et problèmes de performances), on a fini par statuer qu’une simple répartition par colonnes nous suffirait. Puisqu’il n’est pas possible d’agir de cette manière sur les slots en utilisant un template, voilà donc en substance en quoi la solution retenue consiste :

Le code est légèrement simplifié, mais l’idée est là : d’abord créer un élément parent, puis autant d’éléments que de colonnes demandées via la prop columns, et enfin y répartir les vnodes enfants à base de i,j et autres % dignes de mes cours d’IUT. Si vous vous demandez à quoi fait référence ctx , il s’agit d’une particularité des composants fonctionnels.

Le composant s’utilise de cette manière, avec ici un exemple simple de gestion du responsive avec la libraire vue-mq :

vue-enqueue : code & démo

Vue-enqueue

Une autre aventure intéressante fut le problème d’enchaînement de modales pour éviter leur superposition. On a rencontré des cas où il arrive qu’un utilisateur modifie ses données, et ce faisant déclenche d’autres actions (par exemple des récompenses, à la sauce gamification). Pour éviter la frustration d’être coupé dans son travail, on a cherché à retarder l’affichage de la seconde modale une fois la première fermée.

Les premières pistes se sont tournées vers une gestion classique, à savoir une queue via des propriétés data ou encore un store Vuex à part pour gérer les enchaînements. On s’est vite rendu compte qu’on multiplierait le code métier spécifique et aussi les cas particuliers. Sans compter qu’en utilisant le framework ElementUI pour nos interfaces, on a pas complètement la main sur la logique d’affichage de ces composants. On aurait aussi pu jouer avec une directive mais il aurait fallu tricher car on ne peut pas interférer dans le rendu du composant.

On s’est alors rendu compte qu’il était superflu de créer plus de logique métier, et qu’il était plus pragmatique de simplement conditionner l’affichage lui-même. On s’est alors tourné vers la render function via un composant renderless, c’est à dire qui ne génère aucun code en lui-même. Voilà ce que ça donne :

Je mets de côté la logique d’ajout ou de suppression des composants à la queue selon la valeur de this.$slots.default qui n’est pas très intéressante. Par contre je laisse la partie qui englobe le slot dans l’élément transition natif à Vue afin de conserver l’animation à l’affichage des modales, pour montrer qu’on peut facilement s’adapter à des cas particuliers.

Enfin, pour que cette queue soit partagée entre tous les composants qui l’utilisent, il suffit de déclarer la variable en dehors de l’export . Il faut par contre bien le lier ensuite à chaque instance via la propriété data pour conserver la réactivité et ainsi appliquer de nouveau la fonction de rendu quand la variablequeue est modifié. Enfin, l’astuce fonctionne ici parce que queue est un Array, et non pas d’un type primitif comme String ou Number, auquel cas ce ne serait plus la référence vers la variable qui serait utilisée, mais un clone de la valeur en tant que telle. Bref !

On est alors sûrs et certains que deux composants ne seront jamais affichés en même temps et que l’ordre d’affichage est bien respecté, car on est au plus “bas niveau” du cycle de vie de Vue. Il aurait été plus verbeux, spécifique et hasardeux d’opter pour une solution “haut niveau” avec Vuex par exemple. Ce composant s’utilise tout simplement de cette façon :

En bref

Bien qu’elles soient superflues dans 99% des cas et qu’on leur préférera les templates (ou JSX), les fonctions de rendu deviennent indispensables pour le 1% restant. En plus de résoudre des problèmes particulièrement atypiques, apprendre à les utiliser permet de mieux comprendre le fonctionnement de Vue, comme si on jetait pour la première fois un œil sous le capot.

--

--

Nico Prat
nicooprat

Développeur & designer front-end. Freelance & fondateur de @globetrotterio