Comprendre l’API Composition introduite dans Vue.JS 3

Laurent Bernabé

Ce tutoriel a pour objectif de vous initier à la nouvelle Composition API de VueJS, introduite avec la version 3.

Commentez

- Titre : Comprendre l’API Composition introduite dans Vue.JS 3

- Auteur : Laurent Bernabé

- Parution : 3 septembre 2021

- Mise à jour : 19 septembre 2021

- Public visé :

- Licence : Le contenu de cet article est rédigé par Laurent Bernabé et est mis à disposition selon les termes de la Licence Creative Commons Attribution 3.0 non transposé. Les logos Developpez.com, en-tête, pied de page, css, et look & feel de l'article sont Copyright ® 2013 Developpez.com.

Sommaire

- I. Introduction

- II. Éléments de l’API Composition

- II-A. Un maximum de flexibilité

- II-B. Disparition du mot-clé this

- II-C. Données réactives

- II-C-1. Propriété ref (référence)

- II-C-2. Propriété réactive (reactive)

- II-D. Arguments de la fonction setup

- II-D-1. Argument props

- II-D-2. Argument context

- II-E. Propriétés calculées

- II-F. Observateurs

- II-F-1. Observation d’une propriété référence

- II-F-2. Observation d’une valeur d’une propriété réactive

- II-G. Cycle de vie d’un composant

- II-H. API Composition et Vuex

- II-I. API Composition et Vue Router

- II-J. Utiliser l’API Composition avec Vue JS 2

- II-K. La nouvelle syntaxe script setup

- III. Conclusion et remerciements

I - Introduction

Tout d’abord, pour suivre ce tutoriel, vous devez être familiers avec le framework VueJS : la version 2 est largement suffisante. (Voir les tutoriels existants sur le site : Tutoriels VueJS existants sur le site developpez.com)

Avec l’arrivée de la version 3 du framework Vue JS, les développeurs disposent d’une nouvelle façon de coder le script de leurs composants : la Composition API, par opposition à l’Option API, qui elle est déjà présente depuis les versions précédentes.

L’objectif annoncé de cette nouvelle organisation est de permettre de découpler plus facilement les logiques du composant en responsabilités uniques, notamment lorsque le projet prend de plus en plus d’ampleur.

Pour vous donner un exemple très rapide, voici un extrait de code d’un composant écrit dans l’API Option. D’ailleurs, vous pouvez remarquer la définition de trois propriétés data bg_1bg_2, bg_3 et (lignes 19-21), et leurs utilisations dans le template (lignes 3-5).

Bref exemple de script de composant dans l'Option API

1. <template>

2. <div id="app">

3. <MyBox :bg="bg_1" @click="handleClick(bg_1)" />

4. <MyBox :bg="bg_2" @click="handleClick(bg_2)" />

5. <MyBox :bg="bg_3" @click="handleClick(bg_3)" />

6. </div>

7. </template>

9. <script>

10. import MyBox from './components/MyBox.vue'

12. export default {

13. name: 'App',

14. components: {

15. MyBox

16. },

17. data() {

18. return {

19. bg_1: 'yellow',

20. bg_2: 'red',

21. bg_3: 'green',

22. };

23. },

24. methods: {

25. handleClick(bg) {

26. console.log(this.bg_1 + " " + this.bg_2 + " " + this.bg_3);

27. alert(bg);

28. }

29. },

30. mounted() {

31. console.log('Application started');

32. }

33. };

34. </script>

36. <style>

37. #app {

38. display: flex;

39. flex-direction: column;

40. justify-content: space-evenly;

41. align-items: center;

42. }

43. </style>

Où MyBox est un simple composant Vue JS carré de taille fixe, que j’ai importé (ligne 10) et instancié trois fois (lignes 3-5).

Aperçu de l'application exemple

Cette application est basique, à chaque clic sur l’un des carrés, un dialogue de message personnalisé s’affiche (avec en l’occurrence la couleur en anglais du carré cliqué).

Vous pouvez entre autres remarquer l’utilisation du cycle de vie Mounted (lignes 29-31): il exécute la logique ainsi définie dès que le composant en question est monté. Mais vous pouvez aussi remarquer la définition de propriétés (fonction data: lignes 17-23) et de méthodes (section methods : lignes 24-28).

Voici une version possible de ce composant dans l’API Composition.

Bref exemple de script de composant dans la Composition API

1. <template>

2. <div id="app">

3. <MyBox :bg="bg_1" @click="handleClick(bg_1)" />

4. <MyBox :bg="bg_2" @click="handleClick(bg_2)" />

5. <MyBox :bg="bg_3" @click="handleClick(bg_3)" />

6. </div>

7. </template>

9. <script>

10. import MyBox from './components/MyBox.vue'

12. import {onMounted} from 'vue';

14. export default {

15. name: 'App',

16. components: {

17. MyBox

18. },

19. setup() {

20. const bg_1 = 'yellow';

21. const bg_2 = 'red';

22. const bg_3 = 'green';

24. function handleClick(bg) {

25. alert(bg);

26. }

28. onMounted(() => {

29. console.log('Application started');

30. })

32. return {

33. bg_1, bg_2, bg_3, handleClick,

34. };

35. },

36. };

37. </script>

39. <style>

40. #app {

41. display: flex;

42. flex-direction: column;

43. justify-content: space-evenly;

44. align-items: center;

45. }

46. </style>

Tout d’abord, vous pouvez remarquer la déclaration d’une fonction setup (ligne 19) : c’est dans celle-ci que nous définissons les différents éléments de l’API Composition.

Vous pouvez y voir :

- les données réactives (ici nous n’avons que des constantes, mais vous verrez plus tard comment déclarer des données réactives) ;

- les fonctions à utiliser dans le template du composant ;

- les propriétés calculées (ou, en anglais, computed) ;

- les fonctions d’observation (watch) ;

- et les réactions aux différents cycles de vie du composant.

En revanche, les propriétés du composant, ainsi que ses dépendances (clé components) se déclarent comme dans l’API Option.

Ensuite, vous pouvez remarquer que nous retournons dans cette fonction les éléments que l’on souhaite exposer pour le template : les trois constantes bg_* (où l’étoile remplace un numéro) (lignes 20-22), ainsi que la fonction handleClick(lignes 24-26). Pour cela, on retourne un objet où les clés correspondent aux noms des éléments et les valeurs correspondent simplement à leurs valeurs. (Ici j’ai utilisé l’écriture condensée.) (lignes 32-34).

Enfin, l’utilisation du cycle de vie Mounted, par l’utilisation de la fonction onMounted (lignes 28-30), qui accepte une fonction en paramètre, sans argument. Veuillez également noter l’import correspondant (ligne 12).

La fonction setup est exécutée à chaque fois que le composant est monté.

II - Éléments de l’API Composition

Dans ce chapitre, qui ne se veut pas exhaustif, nous allons voir divers éléments de l’API Composition. N’hésitez pas à aller voir la page officielle de Vue JS 3 en cas de besoin : Page officielle de Vue JS 3 : API Composition.

II-A - Un maximum de flexibilité

Tout d’abord, sachez que rien ne vous oblige à utiliser l’API Composition avec Vue 3. Vous pouvez très bien :

- n’utiliser que l’API Option : attention toutefois aux éléments dépréciés par Vue 3 !;

- n’utiliser que l’API Composition ;

- ou bien même mixer les deux : déclarer certains de vos composants avec l’API Option et d’autres avec l’API Composition.

II-B - Disparition du mot-clé this

Vous l’aurez peut-être remarqué dans l’introduction, mais avec l’API Composition on n’utilise plus le mot-clé this. Et pour cause : il n’est plus possible de l’utiliser.

Et pour chaque cas où l’on disposait du mot-clé this dans l’écriture API Option, il existe une solution de remplacement. Y compris pour les fonctions introduites par Vuex (la bibliothèque de gestion de valeurs globales pour Vue) et Vue-Router (la bibliothèque de gestion des pages de votre site Vue JS).

Nous les passerons en revue au fur et à mesure de notre progression dans ce tutoriel.

II-C - Données réactives

Dans l’exemple d’introduction, nous avons remplacé la section data par plusieurs constantes. Mais si l’on devait se limiter aux constantes dans la Composition API, on perdrait à coup sûr en flexibilité au sein de notre application. Heureusement, il est possible de définir et d’exporter des propriétés réactives pour le template.

II-C-1 - Propriété ref (référence)

Le premier type de référence que nous allons aborder est représenté par les références créées avec le mot-clé ref.

Voici un exemple simple :

Simple compteur utilisant des ref

1. <template>

2. <div id="app">

3. <button @click="handleDown">-</button>

4. {{ counter }}

5. <button @click="handleUp">+</button>

6. </div>

7. </template>

9. <script>

10. import {ref} from 'vue';

12. export default {

13. name: 'App',

14. setup() {

15. const counter = ref(0);

17. function handleUp() {

18. counter.value++;

19. }

21. function handleDown() {

22. if (counter.value > 0) counter.value--;

23. }

25. return {

26. counter, handleUp, handleDown,

27. };

28. },

29. };

30. </script>

32. <style>

33. </style>

Tout d’abord, veuillez noter l’import de la fonction ref (ligne 10).

Nous déclarons une variable counter, en tant que référence. (ligne 15). Pour cela, nous appelons la fonction ref, à laquelle nous donnons en paramètre la valeur initiale de notre référence.

La fonction nous retourne donc en quelque sorte un Proxy, c’est-à-dire un objet encapsulant la valeur réelle de la référence. Cela a deux conséquences directes :

1. Au sein de votre fonction setup , la référence devra systématiquement être accédée par l’intermédiaire de sa propriété value. (lignes 18 et 22) ;

2. En revanche, dans le template, chaque référence sera automatiquement convertie par sa valeur réelle : on ne suffixe donc pas le nom de la référence par .value. (ligne 4)

D’ailleurs, c’est cette caractéristique qui fait que j’ai pu déclarer la propriété counter comme étant immuable (const).

Évidemment, tout comme dans la fonction data de l’API Option, il est possible de déclarer et exporter autant de références que l’on souhaite.

II-C-2 - Propriété réactive (reactive)

Admettons que l’on souhaite adapter l’écriture API Option suivante :

Section data avec une propriété complexe.

data() {
return {
john&#160;: {
firstName&#160;: "John",
lastName&#160;: "Doe",
age&#160;: 32,
}
}&#160;;
}

Si l’on devait adapter la propriété john dans l’API Composition avec des ref, on serait obligé de scinder cette propriété en trois références distinctes. Fort heureusement, pour ceux qui souhaitent éviter cela, il existe une autre solution : les propriétés réactives.

Voici un exemple d’utilisation :

Section data avec une propriété complexe : reactive.

1. <template>

2. <div id="app">

3. <input type="text" v-model="john.firstName" />

4. <input type="text" v-model="john.lastName" />

5. <input type="number" v-model="john.age" />

7. <button @click="reset">Reset</button>

9. <p>

10. {{ john.firstName }} {{ john.lastName }}

11. </p>

12. <p>

13. {{ john.age }}

14. </p>

15. </div>

16. </template>

18. <script>

19. import {reactive} from 'vue';

21. export default {

22. name: 'App',

23. setup() {

24. let john = reactive({

25. firstName: "John",

26. lastName: "Doe",

27. age: 32

28. });

30. function reset() {

31. /* Ne fonctionne pas

32. john = {

33. firstName: "John",

34. lastName: "Doe",

35. age: 32

36. };

37. */

38. john.firstName = "John";

39. john.lastName = "Doe";

40. john.age = 32;

41. }

43. return {john, reset};

44. },

45. };

46. </script>

48. <style>

49. #app {

50. display: flex;

51. flex-direction: column;

52. }

53. </style>

Aperçu de l'application

Ainsi :

- j’importe la fonction reactive (ligne 19) et je déclare une propriété john comme étant réactive (ligne 24-28). Attention ! Une référence de type reactive est certes encapsulée derrière un proxy, mais on ne peut pas la réaffecter dans sa globalité. C’est-à-dire que l’on pourra modifier/ajouter des propriétés, mais pas la valeur complète. Donc mieux vaut déclarer ce genre de variables comme étant muable ! (Avec le mot-clé let). Et bien-sûr, je n’oublie pas d’exporter la référence john pour le template (ligne 43) ;

- pour modifier la valeur de la référence réactive dans la fonction setup, il faut obligatoirement modifier les propriétés une à la fois (si toutefois l’on souhaite réellement en modifier plusieurs, voire toutes). Mais en tout cas, inutile d’essayer de modifier les différentes valeurs en attribuant un nouvel objet à la variable, sinon, il n’y aura pas de nouveau rendu du composant déclenché. (lignes 30-41) ;

- enfin, on accède simplement aux valeurs dans le template . (lignes 3-5, 10, 13).

Veuillez également noter, qu’avec une variable de type reactive, si sa valeur au moment de l’exportation dans le bloc return est de type objet ({}), tenter par la suite de la réaffecter dans une valeur primitive (par exemple 2) n’aura pas d’effet : la variable réactive conservera sa précédente valeur. En revanche, rien ne nous interdit de le faire dans la fonction setup, avant l’exportation : mais dans ce cas précis, la variable étant devenue en quelque sorte une valeur primitive, là aussi, tenter de modifier sa valeur par la suite (par un évènement par exemple ou un appel à setTimeoutau sein de la fonction setup), n’aura aucun effet.

Les variables de type reactive semblent donc présenter davantage de subtilités que les variables de type ref.

II-D - Arguments de la fonction setup

La fonction setup peut accepter aucun, un ou bien deux arguments, en fonction de nos besoins. Ces arguments existent notamment parce que dans la fonction setup, l’utilisation du mot-clé this a forcément disparu (Attention, il s’agit de $this avec Vue JS 2, et this avec Vue JS 3).

II-D-1 - Argument props

Le premier argument permet d’accéder aux propriétés du composant. Ainsi, ce code en version API Option :

Composant défini avec l'API Option

1. <template>

2. <div class="myBoxRoot">

3. <div class="myBoxLayer1"></div>

4. <div class="myBoxLayer2" :style="{backgroundColor: bg}">

5. <button @click="alertColor">Alert my color</button>

6. </div>

7. </div>

8. </template>

10. <script>

11. export default {

12. props: {

13. bg: {

14. type: String,

15. default: 'white',

16. },

17. },

18. methods: {

19. alertColor() {

20. alert(this.bg);

21. }

22. }

23. }

24. </script>

26. <style>

27. .myBoxRoot {

28. width: 200px;

29. height: 200px;

30. position: relative;

31. background-color: blue;

32. }

34. .myBoxLayer1 {

35. width: 200px;

36. height: 200px;

37. position: absolute;

38. background-color: yellow;

39. }

41. .myBoxLayer2 {

42. width: 200px;

43. height: 200px;

44. position: absolute;

45. background-color: green;

46. }

47. </style>

où l’on définit la méthode alertColor avec le mot-clé this afin d’accéder à la propriété bg (ligne 20), devra être légèrement adapté si l’on veut utiliser l’API Composition :

Composant défini avec l'API Composition

1. <template>

2. <div class="myBoxRoot">

3. <div class="myBoxLayer1"></div>

4. <div class="myBoxLayer2" :style="{backgroundColor: bg}">

5. <button @click="alertColor">Alert my color</button>

6. </div>

7. </div>

8. </template>

10. <script>

11. export default {

12. props: {

13. bg: {

14. type: String,

15. default: 'white',

16. },

17. },

18. setup(props) {

19. function alertColor() {

20. alert(props.bg);

21. }

23. return {alertColor};

24. }

25. }

26. </script>

28. <style>

29. .myBoxRoot {

30. width: 200px;

31. height: 200px;

32. position: relative;

33. background-color: blue;

34. }

36. .myBoxLayer1 {

37. width: 200px;

38. height: 200px;

39. position: absolute;

40. background-color: yellow;

41. }

43. .myBoxLayer2 {

44. width: 200px;

45. height: 200px;

46. position: absolute;

47. background-color: green;

48. }

49. </style>

Ainsi, nous avons simplement utilisé la méthode setup avec un premier paramètre props, et accédé à la propriété bg de la variable props. (lignes 18-24)

Veuillez également remarquer que le nom props n’est nullement imposé.

II-D-2 - Argument context

Une autre fonctionnalité qui n’est plus disponible à cause de la disparition du mot-clé this est la possibilité d’émettre un évènement depuis notre composant. Et c’est justement pour combler cela que l’on peut déclarer un deuxième argument à la méthode setup.

Pour s’en rendre compte, voici d’abord un composant défini avec l’API Option, ainsi que son utilisation dans un composant Application :

Composant défini dans l'API Option

1. <template>

2. <div class="myBoxRoot">

3. <div class="myBoxLayer1"></div>

4. <div class="myBoxLayer2" :style="{backgroundColor: bg}">

5. <button @click="informMyColor">Alert my color</button>

6. </div>

7. </div>

8. </template>

10. <script>

11. export default {

12. props: {

13. bg: {

14. type: String,

15. default: 'white',

16. },

17. },

18. methods: {

19. informMyColor() {

20. this.$emit('colorInfo', `My color is ${this.bg}`);

21. }

22. }

23. }

24. </script>

26. <style>

27. .myBoxRoot {

28. width: 200px;

29. height: 200px;

30. position: relative;

31. background-color: blue;

32. }

34. .myBoxLayer1 {

35. width: 200px;

36. height: 200px;

37. position: absolute;

38. background-color: yellow;

39. }

41. .myBoxLayer2 {

42. width: 200px;

43. height: 200px;

44. position: absolute;

45. background-color: green;

46. }

47. </style>

Composant App

1. <template>

2. <div id="app">

3. <MyBox bg="red" @colorInfo="handleColorInfo" />

4. </div>

5. </template>

7. <script>

8. import MyBox from './components/MyBox';

9. export default {

10. name: 'App',

11. components: {

12. MyBox,

13. },

14. setup() {

15. function handleColorInfo(msg) {

16. alert(msg);

17. }

19. return {handleColorInfo};

20. }

21. };

22. </script>

24. <style>

25. #app {

26. display: flex;

27. flex-direction: column;

28. }

29. </style>

Par ailleurs, vous pouvez constater qu’il est possible d’utiliser à la fois des composants définis dans l’API Option et d’autres dans l’API Composition.

Vous pouvez donc remarquer en ligne 20 du composant MyBox, l’utilisation de this.$emit afin d’émettre un évènement.

Voici le même composant défini avec l’API Composition.

Composant défini avec l'API Composition

1. <template>

2. <div class="myBoxRoot">

3. <div class="myBoxLayer1"></div>

4. <div class="myBoxLayer2" :style="{backgroundColor: bg}">

5. <button @click="informMyColor">Alert my color</button>

6. </div>

7. </div>

8. </template>

10. <script>

11. export default {

12. props: {

13. bg: {

14. type: String,

15. default: 'white',

16. },

17. },

18. setup(props, context) {

19. function informMyColor() {

20. context.emit('colorInfo', `My color is ${props.bg}`);

21. }

23. return {informMyColor};

24. }

25. }

26. </script>

28. <style>

29. .myBoxRoot {

30. width: 200px;

31. height: 200px;

32. position: relative;

33. background-color: blue;

34. }

36. .myBoxLayer1 {

37. width: 200px;

38. height: 200px;

39. position: absolute;

40. background-color: yellow;

41. }

43. .myBoxLayer2 {

44. width: 200px;

45. height: 200px;

46. position: absolute;

47. background-color: green;

48. }

49. </style>

Ainsi, grâce au deuxième argument context (ligne 18), nous pouvons de nouveau émettre un évènement : context.emit(ligne 20). Même remarque que précédemment : le nom context n’est nullement imposé. Également, rien ne vous empêche d’utiliser le signe underscore en lieu et place du paramètre props ou bien en préfixe, dans le cas où ce paramètre ne vous serait pas utile.

Enfin pour rappel, la fonction emit admet deux arguments :

1. Le nom choisi pour l’évènement ;

2. La valeur à transmettre pour l’évènement : que ce soit une valeur primitive, un tableau ou un objet.

II-E - Propriétés calculées

Il est également possible de définir des propriétés calculées (computed) avec l’API Composition.

À titre d’exemple, reprenons l’exemple précédent, en l’enrichissant un tout petit peu.

Composant utilisant computed dans l'API Composition

1. <template>

2. <div class="myBoxRoot">

3. <div class="myBoxLayer1"></div>

4. <div class="myBoxLayer2" :style="{backgroundColor: bg}">

5. <button @click="informMyColor">Alert my color</button>

6. </div>

7. </div>

8. </template>

10. <script>

11. import {computed} from 'vue';

12. export default {

13. props: {

14. bg: {

15. type: String,

16. default: 'white',

17. },

18. },

19. setup(props, context) {

21. const bangBackground = computed(() => `!!!${props.bg}!!!`);

23. function informMyColor() {

24. context.emit('colorInfo', `My color is ${bangBackground.value}`);

25. }

27. return {informMyColor};

28. }

29. }

30. </script>

32. <style>

33. .myBoxRoot {

34. width: 200px;

35. height: 200px;

36. position: relative;

37. background-color: blue;

38. }

40. .myBoxLayer1 {

41. width: 200px;

42. height: 200px;

43. position: absolute;

44. background-color: yellow;

45. }

47. .myBoxLayer2 {

48. width: 200px;

49. height: 200px;

50. position: absolute;

51. background-color: green;

52. }

53. </style>

Et le composant application App :

Fichier App.vue

1. <template>

2. <div id="app">

3. <MyBox :bg="background" @colorInfo="handleColorInfo" />

4. <button @click="changeColor">Change my color</button>

5. </div>

6. </template>

8. <script>

9. import MyBox from './components/MyBox';

10. import {ref} from 'vue';

12. export default {

13. name: 'App',

14. components: {

15. MyBox,

16. },

17. setup() {

18. function handleColorInfo(msg) {

19. alert(msg);

20. }

22. const background = ref('yellow');

24. function changeColor() {

25. background.value = 'green';

26. }

28. return {background, handleColorInfo, changeColor};

29. }

30. };

31. </script>

33. <style>

34. #app {

35. display: flex;

36. flex-direction: column;

37. }

38. </style>

Dans cette application, en appuyant sur le bouton Alert my color, l’information est mise à jour en fonction de la couleur du composant (que l’on peut changer avec le deuxième bouton).

En ligne 11 du fichier composant, nous importons la fonction computed, et ensuite nous l’utilisons afin de définir une nouvelle propriété bangBackground (ligne 21) à l’aide de cette fonction.

Ainsi cette propriété bangBackground est une référence de type proxy, et nous devons donc accéder à sa valeur par l’intermédiaire de sa propriété value (ligne 24). Cette propriété fonctionne comme les propriétés calculées de l’API Option, se mettant automatiquement à jour avec la valeur de la propriété de base.

Il est même possible de créer une propriété calculée à partir de propriété référence/réactive, voire de mélanger props/refs/reactives dans une même propriété calculée.

Comme dans cette version du composant :

1. <template>

2. <div class="myBoxRoot">

3. <div class="myBoxLayer1"></div>

4. <div class="myBoxLayer2" :style="{backgroundColor: bg}">

5. <button @click="informMyColor">Alert my color</button>

6. </div>

7. </div>

8. </template>

10. <script>

11. import {computed, ref, reactive} from 'vue';

12. export default {

13. props: {

14. bg: {

15. type: String,

16. default: 'white',

17. },

18. },

19. setup(props, context) {

21. const decorator = ref('!!!');

22. let number = reactive({number: 1});

23. const bangBackground = computed(() => `${decorator.value}${props.bg}${decorator.value}-${number.number}`);

25. function informMyColor() {

26. context.emit('colorInfo', `My color is ${bangBackground.value}`);

27. }

29. setTimeout(() => {

30. decorator.value = '@@@';

31. }, 2000);

33. setInterval(() => {

34. number.number++;

35. }, 1000);

37. return {informMyColor};

38. }

39. }

40. </script>

42. <style>

43. .myBoxRoot {

44. width: 200px;

45. height: 200px;

46. position: relative;

47. background-color: blue;

48. }

50. .myBoxLayer1 {

51. width: 200px;

52. height: 200px;

53. position: absolute;

54. background-color: yellow;

55. }

57. .myBoxLayer2 {

58. width: 200px;

59. height: 200px;

60. position: absolute;

61. background-color: green;

62. }

63. </style>

II-F - Observateurs

Il est aussi possible de mettre en place des observateurs (watchers) avec l’API Composition. Il existe plusieurs manières d’utiliser la fonction watch, ici je ne m’attarderai que sur deux cas précis :

1. L’observation d’une propriété référence ;

2. L’observation d’une valeur unique d’une propriété réactive.

II-F-1 - Observation d’une propriété référence

L’observation d’une propriété référence est on en peut plus simple :

1. <script>

2. import {ref, watch} from "vue";

4. export default {

5. setup() {

6. const myRef = ref&#160;;

7. watch(myRef, (newValue, oldValue) => {

8. console.log(newValue)&#160;;

9. console.log(oldValue)&#160;;

10. })&#160;;

11. }

12. }

13. </script>

Le premier paramètre prend donc la référence à observer, et le deuxième paramètre prend une fonction dont les arguments sont dans l’ordre la nouvelle valeur et la valeur précédente.

À noter qu’il est possible de convertir des propriétés reçues dans le composant en références, à l’aide de la fonction toRefs.

1. <script>

2. import {ref, toRefs, watch} from "vue"&#160;;

4. export default {

5. props&#160;: {

6. bg&#160;: {

7. type&#160;: Number,

8. required&#160;: true,

9. },

10. name&#160;: {

11. type&#160;: String,

12. default&#160;: "John",

13. },

14. },

15. setup(props) {

16. const {bg, name} = toRefs(props)&#160;;

17. watch(bg, (newValue, oldValue) => {

18. console.log("new bg: "+newValue)&#160;;

19. console.log("old bg": +oldValue)&#160;;

20. })&#160;;

21. watch(name, (newValue, oldValue) => {

22. console.log("new name&#160;:"+newValue)&#160;;

23. console.log("old name&#160;:"+oldValue)&#160;;

24. })&#160;;

26. return {}&#160;;

27. }

28. }

29. </script>

II-F-2 - Observation d’une valeur d’une propriété réactive

Pour observer une valeur d’une propriété réactive, la syntaxe est à peine plus complexe, car en premier paramètre, il faut cette fois passer une fonction précisant quelle valeur on souhaite extraire :

1. <script>

2. import {reactive, watch} from 'vue'&#160;;

4. export default {

5. setup() {

6. const john = reactive({

7. name: 'john',

8. age: 32,

9. })&#160;;

10. watch(() => john.age, (newValue) => {

11. console.log('John is', newValue, 'years old');

12. })&#160;;

14. setInterval(() => john.age++, 1000);

16. return {}&#160;;

17. }

18. }

19. </script>

Ici je n’ai pas voulu récupérer l’ancienne valeur de john.age dans la fonction passée à watch, mais c’est également possible (à l’instar de l’utilisation de la fonction computed).

II-G - Cycle de vie d’un composant

Pour gérer un cycle de vie à l’intérieur de la fonction setup, rien de plus simple : il suffit d’utiliser la fonction onXXX équivalente. Veuillez noter qu’il n’y a pas d’équivalent pour les cycles de vie beforeCreate et created : c’est déjà le rôle de la fonction setup.

1. <script>

2. import {onBeforeUnmount, onUpdated} from "vue"&#160;;

4. export default {

5. setup() {

6. onUpdated(() => console.log("Updated my component")&#160;;

7. onBeforeUnmount(() => console.log("About to unmount my component")&#160;;

8. return {}&#160;;

9. }

10. }

11. </script>

II-H - API Composition et Vuex

Pour ceux d’entre vous qui utilisent Vuex, le framework de gestion d’état global pour Vue, vous vous êtes sûrement demandé ce qu’il advient de son utilisation avec l’API Composition.

La réponse est simple, dans la dernière version, installable avec npm install vuex@next (évidemment vous pouvez aussi utiliser yarn au lieu de npm), les développeurs ont prévu des fonctions équivalentes afin de pouvoir utiliser Vuex avec l’API Composition. (Notez bien que la version next, correspondait à la version 4.0.2 lors de la rédaction du tutoriel.)

Sans vouloir être exhaustif encore une fois, la documentation officielle est disponible pour cela et sera plus à même de vous présenter des informations mises à jour, dans cette section je vais essayer de répondre à cette problématique.

Tout d’abord, admettons que nous définissons un simple store pour notre application fictive VueJS. Ce store sera le même dans les versions API Option et API Composition de notre exemple. Il s’agit d’ailleurs de l’exemple de la documentation officielle de Vuex 3.

1. const store = new Vuex.Store({

2. state: {

3. count: 0

4. },

5. mutations: {

6. increment (state) {

7. state.count++

8. }

9. }

10. })

Rien de compliqué : il s’agit d’un compteur disposant d’une méthode mutatrice increment.

Je pars du principe que vous savez déjà comment déclarer votre application VueJS comme dépendante de Vuex et de votre store.

Dans l’API Option, afin d’émettre une mutation de type increment sur notre store, nous pouvons nous y prendre de la manière suivante :

1. methods: {

2. increment() {

3. this.$store.commit('increment')

4. console.log(this.$store.state.count)

5. }

6. }

Évidemment, pour l’API Composition, le problème réside dans l’utilisation du mot-clé this.

Pour contourner ce problème, il suffit donc d’importer la fonction depuis Vuex (que vous aurez bien sûr pensé à mettre à jour) afin de créer une référence sur notre store et la fonction computed afin de surveiller les modifications du store :

1. import { computed } from 'vue';

2. import { useStore } from 'vuex';

4. export default {

5. setup () {

6. const store = useStore()

8. const increment = computed(() => {

9. store.commit('increment');

11. // Evidemment d&#233;conseill&#233;

12. console.log(store.state.count);

13. });

15. return {

16. increment,

17. }

18. }

19. }

N’oubliez pas également qu’accéder directement aux états du store est considéré comme une mauvaise pratique.

De la même manière, vous disposez de la fonction store.dispatch afin notamment de faire appel à des mutations asynchrones du store.

Encore une fois, n’hésitez pas à consulter la documentation officielle.

II-I - API Composition et Vue Router

Le framework Vue Router, pour gérer la navigation dans les différentes pages de votre site Single Page Application, est également « impacté » par l’utilisation de l’API Composition, mais la transition, à l’instar de l’utilisation de Vuex, n’est pas très complexe.

Pour cela, il faut utiliser une version récente de Vue Router : lors de la rédaction, la version next correspondait à la version 4.0.11 . La documentation officielle est également là pour vous aider dans votre éventuelle transition.

Là aussi, je pars du principe que vous savez utiliser Vue Router et que vous savez la configurer pour votre application.

Dans l’API Option, vous disposez de deux objets très importants depuis le mot-clé this : this.$route et this.$router.

Dans l’API Composition, il suffit :

1. D’importer les fonctions useRoute et useRouter depuis la librairie vue-router ;

2. De créer une référence à partir de ces fonctions ;

3. D’appeler les fonctions disponibles depuis ces références presque comme l’on ferait depuis l’API Option.

Voici un court script d’exemple dans l’API Composition :

1. import { useRouter, useRoute } from 'vue-router'

3. export default {

4. setup() {

5. const router = useRouter()

6. const route = useRoute()

9. const exampleUseFunction() {

10. // ...

11. const query = route.query;

12. const params = route.params;

13. // ...

14. }

16. return {

17. exampleUseFunction,

18. }

19. }

De même, il existe des fonctions pour les navigation guards, notamment beforeRouteLeave, beforeRouteUpdate (exemple adapté depuis la documentation officielle):

1. import { onBeforeRouteLeave } from 'vue-router'

2. import { ref } from 'vue'

4. export default {

5. setup() {

6. // from is from where we try to exit

7. // to is the destination page

8. onBeforeRouteLeave((to, from) => {

9. const answer = window.confirm(

10. 'Do you really want to leave? you have unsaved changes!'

11. )

12. // cancels the navigation and stays on the same page

13. if (!answer) return false

14. })

16. return {};

18. },

19. }

II-J - Utiliser l’API Composition avec Vue JS 2

Sachez qu’il est possible d’utiliser l’API Composition avec la version 2 de Vue JS. Pour cela vous pouvez installer le plugin Vue JS @vue/composition-api, notamment à l’aide de votre gestionnaire de paquets NPM ou Yarn le cas échéant.

Par contre, cela ne va pas sans limitation : vous pouvez les retrouver sur la page NPM du paquet.

II-K - La nouvelle syntaxe script setup

Si vous écrivez des composants définis en uniques fichiers (Single File Component en anglais), vous pouvez en plus utiliser la syntaxe « script setup ».

Il s’agit d’un simplification de l’API Composition dans laquelle notamment

- l’instruction return n’est plus nécessaire ;

- l’ensemble des éléments déclarés est exporté pour la partie template ;

- tout code déclaré dans cette section est exécuté à chaque fois que le composant est créé.

Voici un bref exemple, extrait de la documentation officielle :

<script setup>
// variable
const msg = 'Hello!'

// functions
function log() {
console.log(msg)
}
</script>

Ainsi la variable msg et la fonction log seront directement disponibles dans la partie template du composant fichier unique.

En revanche, il est fort probable que la plupart des éditeurs en ligne (tels que StackBlitz) ne reconnaissent pas encore cette syntaxe.

III - Conclusion et remerciements

Voilà, c’est la fin de cette brève introduction sur l’API Composition de Vue JS 3.

N’hésitez pas à consulter la documentation officielle.

Je tiens à remercier Mickaël Baron pour sa relecture technique.

Je tiens aussi à remercier escartefigue pour sa relecture orthographique.

Je souhaite également remercier SylvainPV et gwyohm pour leurs remarques très pertinentes sur le forum.