Introduction
Il y a quelques années, comme beaucoup d'autres, j'étais "hypé" par l'arrivée des est déjà pris en charge, un système de routage et un store sont proposés, et il y a même de quoi réaliser des tests unitaires et End-to-End !
Par défaut, c'est le bundler
Et pour la mise en prod ? Il suffit de saisir la commande npm run build, et le projet s'exporte en fichiers statiques dans un répertoire dist après avoir vérifié les typages (dans le cas où l'on a activé le Typescript) :
vite v5.2.11 building for production...
✓ 48 modules transformed.
dist/index.html 0.43 kB │ gzip: 0.28 kB
dist/assets/AboutView-C6Dx7pxG.css 0.09 kB │ gzip: 0.10 kB
dist/assets/index-D6pr4OYR.css 4.21 kB │ gzip: 1.30 kB
dist/assets/AboutView-CEwcYZ3g.js 0.22 kB │ gzip: 0.20 kB
dist/assets/index-CfPjtpcd.js 89.23 kB │ gzip: 35.29 kB
✓ built in 591ms
Architecture du projet
.
├── README.md
├── e2e/
├── index.html
├── package.json
├── public/
├── src/
│ ├── App.vue
│ ├── assets/
│ ├── components/
│ │ ├── HelloWorld.vue
│ │ ├── TheWelcome.vue
│ │ ├── WelcomeItem.vue
│ │ ├── __tests__/
│ │ └── icons/
│ ├── main.ts
│ ├── router/
│ │ └── index.ts
│ ├── stores/
│ │ └── counter.ts
│ └── views/
│ ├── AboutView.vue
│ └── HomeView.vue
├── tsconfig.json
└── vite.config.ts
Côté architecture du projet, on trouve notamment :
- le fichier
index.html, avec la balise<div id="app"></div>sur laquelle vient se greffer toute notre application Vue ; - le
main.ts, avec la création successive du composant App, du router et du store :
import './assets/main.css';
import { createApp } from 'vue';
import { createPinia } from 'pinia';
import App from './App.vue';
import router from './router';
const app = createApp(App); // composant racine
app.use(createPinia()); // store
app.use(router); // routage des pages front
app.mount('#app');
- des fichiers
.tspurs, pour gérer le routage et le store ; - quelques fichiers de config et de test ;
- ... et bien sûr les fichiers
*.vue, distingués en components (qui correspondent plutôt à des éléments génériques et réutilisables), et views (qui correspondent plutôt à des pages haut niveau)
En bref, l'architecture des fichiers est plutôt simple et relativement similaire à celle de React, même en ayant coché pas mal d'options dans le boilerplate.
Jusque-là, venant de React, rien de bien nouveau. C'est ensuite qu'apparaissent des différences significatives !
Architecture d'un fichier Vue
Voici un extrait de code inspiré du .
De mon point de vue, je trouve que ce genre de librairie "all-in-JS" casse un peu l'architecture que propose Vue, et de toute évidence, les propriétés CSS spécifiques aux navigateurs sont beaucoup plus rares de nos jours. La balise <style> de Vue est donc souvent autosuffisante.
Bref, j'ai trouvé très plaisante cette architecture tout-en-un mais avec des sections bien séparées. Cela permet de garder un code propre, mais aussi plus concis. En effet, la présence simultanée des 3 sections "logique métier / affichage / style" incite souvent à redécouper son code en plus petits modules, et donc en plus petits fichiers.
Maintenant, si on se penchait un peu plus sur l'API Vue.js elle-même ?
L'API Vue.js
Ici je ne vous ferai pas la liste exhaustive de tous les éléments de l'API Vue.js que j'ai pu rencontrer, mais seulement de certains, bien spécifiques, que j'ai trouvés assez représentatifs de la logique de Vue.
Les valeurs (re)calculées
Commençons par une opération bien connue de l'univers React : recalculer intelligemment un rendu HTML (ou une variable) suite à la mise à jour d'une donnée.
Il y a la fonction très intuitive computed() qui bénéficie d'un système de mémoïsation (sorte de "cache") pour éviter de recalculer à chaque fois la valeur de sortie :
Seul un clic sur le bouton modificateur de num va incrémenter le compteur de "watchs".
Le watch() nous permet alors de déclencher une callback à chaque fois que certaines variables changent.
La force de cette fonction réside dans l'analyse en profondeur des modifications de variable : Vue détecte les changements même au fin fond d'un sous-objet !
La synchronisation bidirectionnelle
Déclarer et transmettre une propriété d'un composant parent à un composant enfant est une opération assez récurrente. Synchroniser cette valeur entre l'enfant et le parent l'est également, par exemple dans l'input un formulaire.
Aussi, plutôt que de gérer à la fois une propriété et une callback de mise à jour sur évènement comme ici :
<!-- Child.vue -->
<script setup>
const props = defineProps(['myModelValue']); // déclaration propriété
const emit = defineEmits(['update:myModelValue']); // déclaration callback
</script>
<template>
<h3>Textfield:</h3>
<input
:value="props.modelValue"
@input="emit('update:myModelValue',
($event.target as HTMLInputElement).value)"
/>
</template>
… il est possible d'utiliser à la place la macro defineModel qui permet de simplifier l'écriture :
<!-- Child.vue -->
<script setup>
const myModelValue = defineModel('myModel');
</script>
<template>
<h3>Textfield:</h3>
<input v-model="myModelValue" />
</template>
Beaucoup plus court ! 😎 D'ailleurs, n'ayant qu'un seul modèle, j'aurais même pu me dispenser de le nommer !
Et avec le parent :
<!-- Parent.vue -->
<script setup>
import { ref } from 'vue';
import Child from './Child.vue';
const data = ref('my default value');
</script>
<template>
<div>Parent value: {{data}}</div>
<Child v-model:my-model="data"></Child>
</template>
Comme on pouvait s'y attendre, l'instruction v-for permet donc de répéter automatiquement une partie d'un patron HTML (ici la balise <li>) pour chaque élément d'un itérable.
Côté React, il aurait fallu utiliser du JSX pour construire soi-même chaque élément, rendant le code moins lisible au fur et à mesure que le composant grandit :
import React from 'react';
const commonGitActions = ['Pull', 'Commit', 'Push', 'Merge', 'Rebase'];
export function App(props) {
return (
<>
<h1>Actions git communes</h1>
<ul>
{commonGitActions.map((action) => (
<li key={action}>{action}</li>
))}
</ul>
</>
);
}
Personnellement, j'ai une préférence pour la structure de Vue en termes de propreté de code, tant qu'il n'y a pas besoin de déboguer. 😅
Et d'ailleurs, puisqu'on parle de déboguer, qu'en est-il des outils de l'écosystème Vue ?
Les outils de dev
Voici 3 outils qui ont retenu mon attention dans le développement de mon projet.
Extension VSCode : Vue Official
Je commence par une évidence, mais oui, il y a bien une extension pour VSCode Vue.js devtools, qui, je dois l'avouer, est déjà très bien fourni :
et fournit également une liste d'icônes standards :
Cela permet de gagner beaucoup de temps sur des démarrages de projet, ou sur des projets qui ne nécessitent pas de personnalisation graphique trop poussée.
Mais comme toujours, je recommande de garder un œil sur les performances de rendu avec ce genre de librairie haut niveau. La capacité de configuration d'une librairie se paie souvent ailleurs !
Conclusion
Que dire de cette expérience de migration de React vers Vue ?
Tout d'abord, d'un point de vue du code, par rapport à React je dirais que la lib Vue est :
plus structurée ;
plus déclarative ;
plus concise.
Toutefois, grâce à son code qui s'écrit plutôt en JSX, je trouve que React reste beaucoup plus interopérable, plus programmatique et plus explicite que Vue, et avec une meilleure stabilité du linter.
Côté environnement de développement et communauté, Vue a pour moi toutes les cartes en main pour assurer des développements efficaces jusqu'à la mise en prod.
Alors est-ce que cette montée en compétence sur Vue a été à la hauteur de sa notoriété ? Je dirais que oui. J'ai trouvé la courbe d'apprentissage efficace, et je continuerai de développer avec Vue si l'occasion se présente.
Enfin, est-ce qu'aujourd'hui il vaut mieux développer du frontend en Vue qu'en React ?
D'un point de vue totalement personnel, je pense que non. Même si Vue et React ont chacun des cas d'application un peu différents, je préfère me fier à un système de typage fiable et à un code plus souple avec React. Mais peut-être que les prochaines versions de Vue et leurs outils me feront changer d'avis ?
Et vous, quels sont vos retours d'expérience ?
SOCIAL SHARE CARD GENERATOR