Vuejs 3 : présentation de la composition api
Vue 3 change la donne. Maintenant mainstream cette release apporte des changements majeurs concernant l'architecture d'un projet Vue. La nouvelle composition api permet d'adopter une organisation du code plus fluide, plus proche d'une logique métier. A noter le support de premier ordre de Typescript dans cette version. Les exemples de code dans cet article utilisent Typescript. Le code des exemples contenu dans cet article est disponible sur Github
Les bases de la composition api
Le code d'un composant est entièrement contenu dans un objet setup
. A la différence de la classique options api le code n'a pas à être divisé en unités fonctionnelles (data
, methods
, computed
, ...). Ceci permet l'abolition du keyword this
et de son ambiguïté liée au contexte. Exemple de déclaration de composant:
import { defineComponent } from "vue";
export default defineComponent({
setup() {
// le composant est défini ici
}
});
Ajoutons des data et des méthodes comme nous le ferions avec la options api. Tout s'effectue dans setup
. Noter l'objet retourné par la fonction, qui va exposer les éléments que l'on souhaite utiliser dans le template:
import { defineComponent } from "vue";
export default defineComponent({
setup() {
// comme data dans l'options api
const myvar = 3;
// définition d'une méthode
function increment(): number {
return myvar + 1;
}
// l'objet retourné contenant les éléments qui
// seront exposés dans le template
return {
myvar,
increment
}
}
});
Nouveau système de réactivité
Vue 3 propose un nouveau système indépendant de gestion de la réactivité. Basé sur les proxy objects ES6 il offre trois types principaux d'objets: ref
, reactive
et computed
.
L'objet ref: la base
L'objet ref
est réactif et peut être lu et écrit. Cela permet de définir une variable réactive mutable. L'avantage de cet objet est son caractère explicite, via l'utilisation de sa propriété value
. Exemple:
setup() {
// on définit une variable réactive
const myvar = ref<number>(1);
// pour lire cette ref on utilise le mot clé value
console.log(myvar.value);
// une méthode pour agir sur la variable
function mutateFoo(): number {
// noter l'utilisation de value pour muter une ref
++myvar.value;
}
return {
myvar,
mutateFoo
}
}
Créons un template et utilisons la variable exactement comme on le ferait avec le classique data
:
Ref value: {{ myvar }}
Rendu du template:
La mutation sur ref sera reflétée dans le template
L'objet reactive: pour les data structures plus complexes
L'objet reactive
sert a gérer les variables de type objet et offre une réactivité en profondeur: il est possible de muter seulement une propriété d'un objet reactive
.
setup() {
// on définit une variable réactive pour un objet
const state = reactive({
counter: {
currentValue: 1,
max: 10,
},
});
// pas besoin d'utiliser le keyword value comme pour ref
// pour accéder à la valeur de la variable
console.log(`current counter value is ${state.counter.currentValue}`);
// définissons une méthode pour muter une propriété
// de la variable réactive state
function increment() {
if (state.counter.currentValue < state.counter.max) {
++state.counter.currentValue;
} else {
console.log("The counter has reached the maximum");
}
}
return {
state,
increment,
};
},
La variable réactive s'utilise dans un template de la même manière
Count: {{ state.counter.currentValue }}
The counter has reached the max
En résumé: cet objet est pratique pour les structures imbriquées et rend les propriétés réactives. Il ne nécessite pas l'utilisation du keyword value
comme pour ref
.
L'objet computed: pour les variables réactives en lecture seule
Cet objet fonctionne exactement comme la section computed
dans l'option api classique. Une computed
variable sera en lecture seule. Complétons le code ci-dessus avec une computed property:
setup() {
const state = reactive({
counter: {
currentValue: 1,
max: 10,
},
});
// on crée une computed property pour lire le state
const isCounterBlocked = computed(() => {
return state.counter.currentValue >= state.counter.max;
});
// le mot clé value est utilisé ici comme pour ref
console.log(`current is blocked ${isCounterBlocked.value}`);
function increment() {
// on utilise ici la computed prop
if (!isCounterBlocked.value === true) {
++state.counter.currentValue;
} else {
console.log("The counter has reached the maximum");
}
}
return {
state,
increment,
isCounterBlocked,
};
},
L'utilisation en template reste classique :
The counter has reached the max
State management: les nouvelles possibilités
Le système de réactivité indépendant de Vue 3 permet de simplifier le state management. Cela rend possible la définition d'un state local ou global utilisable par plusieurs composants. Utilisons une variable de type reactive
pour définir un state local. Dans un fichier state.ts:
import { reactive } from "@vue/reactivity";
// ce state pourra être importé dans les composants
const state = reactive({
counter: {
currentValue: 1,
max: 10,
}
});
export default state;
Utilisons ce state dans des composants distincts. On importe le state dans le composant 1 et on définit une mutation:
Count: {{ state.counter.currentValue }}
Dans le composant 2 le state sera réactif à la mutation effectuée dans le composant 1:
Count: {{ state.counter.currentValue }}
Note: le compteur ne sera pas réinitialisé en cas de navigation et de retour sur la page: le state étant indépendant du composant et stocké en mémoire il sera préservé lors de la navigation.
Ce pattern simple permet une gestion fine du state et de sa portée. Il convient particulièrement pour des applications simples et permet de s'affranchir de la complexité d'un store Vuex. Pour illustrer la flexibilité possible dans l'exemple ci-dessus la mutation du state a lieu directement dans le composant 1 mais pourrait être définie dans le state.ts, suivant le pattern classique du store.
Notons que l'utilisation du pattern du store et les garanties qu'il apporte reste tout à fait possible pour les applications plus importantes.
Conclusion
Vue 3 apporte beaucoup de par sa composition api. Une organisation du code plus flexible et plus centrée sur la logique métier devient possible. Le système de réactivité apporte de son coté une souplesse bienvenue en termes de state management.
Enfin l'excellent support de Typescript dans la composition api apporte avec lui toutes les garanties du typage statique, rendant les applications plus solides.
Le code des exemples figurant dans cet article est disponible sur Github