Développement web avec Vue.js

Mickaël BARON - Version 09-2023

baron.mickael@gmail.com ou baron@ensma.fr

Licence d'utilisation

Creative Commons

Contrat Paternité

Partage des Conditions Initiales à l'Identique

2.0 France

creativecommons.org/licenses/by-sa/4.0

Qui suis-je ?

Ingénieur de Recherche ☀️

  • Recherche dans l'équipe Ingénierie des Données et des moDèles (IDD)
  • Valorisation des plateformes logicielles
  • « Coach technique » auprès des usagés

Ancien Responsable 🌑 (2011-2021)

  • Rubriques : Java, Java Web, Android, Eclipse, Spring et Netbeans
  • Rédacteurs de tutoriels
  • Chiffres
    • 4 M de visiteurs
    • 12 M pages vues/mois
    • 7500 membres
    • 2000 forums

Objectif du cours Vue.js (45 min)

  • Modèle et vue
  • Templating (interpolation et directives)
  • Composants (créer et instancier)
  • Communiquer entre composants
  • Routage
  • Outils pour Vue.js

Disclaimer 🧎🏻‍♂️

Pas le temps ⏳ de tout traiter

  • Appeler des services web
  • Tester
  • Déployer

Expérimentation via des exercices

Prérequis minimum

  • Langages à balises (XML, HTML)
  • Notions élémentaires JavaScript

Exemples du cours

github.com/mickaelbaron/vuejs-examples

Évolution du cours

Version 2023

  • Communication bidirectionnelle avec V-MODEL pour composant

Version 2022

  • Support Vue.js 3.x
  • Passage à API Composition
  • Ajout des exemples sur Github

Version supportée dans ce cours

3.x

Historique

Origine

  • Projet open source
  • Evan You, ancien Google, a travaillé sur Angular
  • /vuejs/vue
  • Léger : ~ moins de 33 KB min (minifiée + gzip)

Historique des versions : version actuelle 3

Popularité

Source : stateofjs.com

Qui utilise Vue.js ?

Source : vue2-introduction

Où trouver de la documentation ?

Mon site

Le site de référence

HelloWorld en Vue.js

<!-- Chargement de la bibliothèque -->
<script src="https://unpkg.com/vue@3"/>
						
<div id="helloworld">
 <input v-model="value" type="text" /> 
 <p>{{ value }}</p>
</div>
	
<script>
 const { createApp, ref } = Vue

 createApp({
  setup () {
   const value = ref('Helloworld')

   return { value }
 }}).mount('#helloworld');
</script>

Dossiers helloworldv3-sfc (version SFC) et helloworldv3 (version sans outil)

HelloWorld en Vue.js

{{value}}

Versatile

Deux utilisations différentes

Bibliothèque

Votre code intègre Vue.js

Framework

Vue.js intègre votre code

Vue.js comme bibliothèque

Quand ?

  • Petits projets
  • Dans un code existant (formulaire)

Comment ?

<script src="https://unpkg.com/vue@3">

Avec quoi ?

  • Aucun outil supplémentaire (utilisable maintenant)

Exemples

Vue.js comme framework

Quand ?

  • Projets importants
  • Single-Page-Application (SPA)

Comment ?

  • Fichier Single-File-Component

Avec quoi ?

$ npm create vite@latest

Versatile

Pour chaque utilisation une écriture différente basée sur l'API Composition

Bibliothèque

<html>
 <body>
  <script src="https://unpkg.com/vue@3"></script>
  <div id="helloworld">
   <input v-model="value" type="text" />
   <p>{{ value }}</p>
  </div>

  <script>
   const { createApp, ref } = Vue
   createApp({
    setup() {
     const value = ref('HelloWorld')
     return { value }
    }
   }).mount('#helloworld');
  </script>
 <body>
<html>

Framework

<script setup>
 import { ref } from 'vue'
  
 const value = ref('HelloWorld')
</script>

<template>
 <div id="helloworld">
  <input v-model="value" type="text" />
  <p>{{ value }}</p>
 </div>
</template>

<style>
#helloworld {
 text-align: center;
 text-decoration: underline;
}
</style>

Versatile : attention

L'évolution de Vue.js a introduit différentes écritures pour développer un composant

  • API Options (depuis Vue.js 1 et 2)
  • API Composition (depuis Vue.js 3)

Les différentes API (Options et Composition) peuvent cohabiter

Pour éviter de s'y perdre nous utiliserons l'API Composition

Modèle et vue

Inspiration du modèle d'architecture MVVM
Model-View-ViewModel

Vue.js correspond à la partie ViewModel

Modèle et vue

HelloWorld et le modèle d'architecture MVVM

Différents concepts clés de Vue.js étudiés dans la suite

  • Interpolation : {{ value }}
  • Directive : v-model="value"
  • Propriétés réactives : value
  • Chaque MVVM est un composant

Propriétés réactives

La vue est rafraichie à chaque fois que les propriétés du composant changent

Utilisation de propriétés reactives qui peuvent notifier la vue de changements

Deux mots clés disponibles fournies par Vue.js

  • ref : pour les valeurs primitives
  • reactive : pour les objets

Nécessite d'importer ces mots clés pour les utiliser

Propriétés réactives : ref

Quand ?

  • Propriétés avec des valeurs primitives

Comment déclarer ?

const property = ref('Helloworld')

Comment accéder ou modifier ?

  • C'est la valeur (value) de la référence qui est modifiée ou obtenue
  • Utilisation de property.value

Propriétés réactives : ref

<div id="app">
 <input v-model="myprop" type="text" />
 <p>{{myprop}}</p>
</div>

<script>
 // Importation du mot clé ref
 const { createApp, ref } = Vue

 createApp({
  setup() {
   const myprop = ref('HelloWorld')
   // Lecture de la valeur de myprop
   console.log(myprop.value)
   // Modification de la valeur de myprop
   myprop.value = "HelloWorld2"
   return { myprop }
  }
 }).mount('#app');
</script>

Fichier indexref.html (version sans outil)

{{value}}

Propriétés réactives : reactive

Quand ?

  • Propriétés de type objet

Comment déclarer ?

const property = reactive({ value: 'Helloworld' })

Comment reactive fonctionne ?

  • Création d'un proxy sur l'objet
  • Pas besoin d'utiliser .value
  • toRef et toRefs pour transformer en ref

Propriétés réactives : reactive

<div id="app">
 <input v-model="data.myprop" type="text" />
 <p>{{data.myprop}}</p>
</div>

<script>
 // Importation du mot clé reactive
 const { createApp, reactive } = Vue

 createApp({
  setup() {
   const data = reactive({ myprop: 'HelloWorld' })
   // Lecture de la propriété myprop de data
   console.log(data.myprop)
   // Modification de la propriété myprop de data
   data.myprop = "HelloWorld2"
   return { data }
  }
 }).mount('#app');
</script>

Fichier indexreactive.html (version sans outil)

{{data.myprop}}

Propriétés réactives : reactive

Une seule propriété réactive avec toRefs

<div id="app">
 <input v-model="myprop1" type="text" />
 <p>{{myprop1}}</p>
 <input v-model="myprop2" type="text" />
 <p>{{myprop2}}</p>
</div>

<script>
 // Importation du mot clé toRefs
 const { createApp, reactive, toRefs } = Vue

 createApp({
  setup() {
   const data = reactive({ 
    myprop1: 'HelloWorld1', 
    myprop2: 'HelloWorld2' })

   return { ...toRefs(data) }
  } 
}).mount('#app');
</script>

Fichier indexreactivetorefs.html (version sans outil)

{{myprop1}}

{{myprop2}}

Templating avec Vue.js

Comment réaliser le databinding ?

Interpolation

Directive

Interpolation Vue.js

Liaison unidirectionnelle : modèle ➡️ vue

Utilisation de la notation moustache : {{ ... }}

Exemples d'interpolation
{{ myProperty }}
{{ number + 1 }}
{{ ok ? 'OUI' : 'NON' }}
{{ message.split('').reverse().join('') }}

Limites

  • N'interpètre pas du HTML (voir v-html)
  • Inutilisable dans des attributs HTML (voir v-bind)

Directives Vue.js

Attributs HTML spécifiques à Vue.js

  • v-model
  • v-bind
  • Directives conditionnelles
    • v-if
    • v-else
    • v-else-if
    • v-show
  • v-for
  • v-on
  • v-html
  • v-text
  • v-cloak
  • v-pre
  • v-once

Étudions ces directives par l'exemple

Directive : v-model

Liaison bidirectionnelle (modèle ↔️ vue) pour composants <input> <select> <textarea>

<div id="app">
 <input v-model="value"/>
 <textarea v-model="value"/>
 <p>{{value}}</p>
</div>

<script>
 createApp({
  setup() {
   const value = ref('Helloworld')
   return { value } 
  }
 }).mount('#app')
</script>

Fichier vmodel.html (version sans outil)

{{value}}

Directive : v-bind

Liaison unidirectionnelle (modèle ➡️ vue) utilisable dans des attributs HTML (ex. : value, disabled)

Syntaxe 👉 v-bind:attribute...
Syntaxe simplifiée 👉 :attribute...

<div id="app">
 <input v-model="value" />
 <input v-bind:value="value" />
 <input :value="value" />
 <p>{{value}}</p>
</div>
<script>
 createApp({
  setup() {
   const value = ref('Helloworld')
   return { value } 
 }}).mount('#app')
</script>

Fichier vbind.html (version sans outil)

{{value}}

Directives conditionnelles

Cacher ou afficher un élément HTML

v-if, v-else et v-else-if

  • Effectuer un rendu d'un élément
  • v-else et v-else obligatoirement après v-if

v-show

  • Masquer un élément via la propriété CSS display

Comment choisir entre v-show et v-if ?

  • Permutations fréquentes 👉 v-show

Directives conditionnelles

<div id="app">
 <input v-model="show">
 Afficher Vue.js

 <img v-show="show" src="lias.png"/>

 <img v-if="show" src="vue.svg"/>
 
 <img v-else src="mbaron.jpg"/>
</div>

<script>
 createApp({
  setup() {
   const show = ref(true)
   return { show } 
  }
 }).mount('#app')
</script>

Fichier vifvshow.html (version sans outil)

Afficher Vue.js

Directive : v-for

Effectuer plusieurs fois le rendu d'une liste d'éléments

Tableau (items)

v-for="(item, index) in items"
  • item : élément courant (ex. : rouge)
  • index : position (ex. : 1)

Objet (object)

v-for="(value, name, index) in object"
  • value : valeur de la propriété (ex. : mickaël)
  • name : nom de la propriété (ex. : firstname)

Directive : v-for

<div id="app">
 <p v-for="(val,index) in tab">
  {{val + "," + index}}
 </p>

 <p v-for="(val,name,index) in obj">
  {{val + "," + name + "," + index}}
 </p>
</div>

<script>
 createApp({
  setup() {
   const tab = ref(['rouge','bleu'...])
   const obj = ref({p1:'val1',p2:'val2'...})
   return { tab, obj }
  }
 }).mount('#app')
</script>

Fichier vfor.html (version sans outil)

{{ val+","+index }}

{{ val + "," + name + "," + index }}

Directive : v-on

Écouter les événements de la partie View pour éxécuter vos traitements

Syntaxe 👉 v-on:event=...
Syntaxe simplifiée 👉 @event=...

Modificateurs d'événements

  • .stop : un évènement stoppé
  • .prevent : rechargement page stoppé
  • .left : uniquement bouton gauche de la souris
  • .once : une seule fois
  • ...

Directive : v-on

<div id="app">
 <button v-on:click="increase">
  Augmenter
 </button>
 <button @click.once="decrease">
  Diminuer
 </button>
 <p>{{ value }}</p>
</div>
<script>
 createApp({
  setup() {
   const value = ref(0)
   function increase() {
    value.value++;
   }
   function decrease() {
    value.value--;
   }
   return { value, increase, decrease }
  }
 }).mount('#app')
</script>

Fichier von.html (version sans outil)

{{ value }}

Autres directives

v-html : interprète du contenu HTML

v-text : identique à l'interpolation de texte {{ }}

v-once : effectue le rendu une seule fois

v-cloak : bloque l'affichage tant que la compilation Vue.js n'est pas terminée

v-pre : n'effectue pas de compilation, texte brute

v-??? : possibilité de créer ses propres directives personnalisées : en savoir plus

Autres directives

<div id="app">
 <div v-html="html"></div>

 <div v-text="html"></div>
 
 <div v-once>{{value}}</div>
 <input v-model="value" />
 
 <div v-pre>{{value}}</div>
</div>

<script>
 createApp({
  setup() {
   const html = ref('<b>HelloWorld</b>')
   const value = ref('HelloWorld')

   return { html, value }
  }
 }).mount('#app-directives');
</script>

Fichier vhtmltextoncepre.html (version sans outil)

{{value}}
{{value}}

Composant

Mise en place d'un composant ?

  1. Savoir développer un composant ?
    Single-File-Component
  2. Savoir instancier un composant ?
    Balise personnalisée
  3. Savoir écouter un composant ?
    Propriétés calculées et observateurs
  4. Savoir contrôler un composant ?
    Cycle de vie

Développer un composant ?

Deux manières de développer des composants

Bibliothèque

<html>
 <body>
  <script src="https://unpkg.com/vue@3"></script>
  <div id="helloworld">
   <input v-model="value" type="text" />
   <p>{{ value }}</p>
  </div>

  <script>
   const { createApp, ref } = Vue
   createApp({
    setup() {
     const value = ref('HelloWorld')
     return { value }
    }
   }).mount('#helloworld');
  </script>
 <body>
<html>

Framework

<script setup>
 import { ref } from 'vue'
  
 const value = ref('HelloWorld')
</script>

<template>
 <div id="helloworld">
  <input v-model="value" type="text" />
  <p>{{ value }}</p>
 </div>
</template>

<style>
#helloworld {
 text-align: center;
 text-decoration: underline;
}
</style>

Développer un composant ?

Fichier Single-File-Component via format .vue

Script : ViewModel

  • JavaScript
  • Lien avec le Modèle

Template : View

  • HTML
  • Interpolation et directive

Style

  • CSS localisé
<script setup>
 import { ref } from 'vue'
  
 const value = ref('HelloWorld')
</script>

<template>
 <div id="helloworld">
  <input v-model="value" type="text" />
  <p>{{ value }}</p>
 </div>
</template>

<style>
#helloworld {
 text-align: center;
 text-decoration: underline;
}
</style>

Instancier un composant ?

Deux étapes à réaliser depuis un fichier .vue

① Importation (dans la partie script)

import YourComponent1 from "./components/yourcomponent1.vue"
import YourComponent2 from "./components/yourcomponent2.vue"
...

② Instanciation (dans la partie template)

<YourComponent1></YourComponent1>
<YourComponent2></YourComponent2>

Instancier un composant ?

<script setup>
 import { ref } from 'vue'
 import Clock from './components/Clock.vue'

 const value = ref("HelloWorld")
</script>

<template>
 <Clock></Clock>

 <input v-model="value" type="text" /> 
 <p>{{ value }}</p>
</template>

<style/>

Dossiers customelementv3-sfc (version SFC) et customelementv3 (version sans outil)

{{ value }}

Ecouter composant : calculée

Problématiques

  • Expressions complexes dans la partie View
  • Expressions utilisées plusieurs fois

Propriété calculée (computed)

  • Dépendances vers autres propriétés réactives

Syntaxe

const yourProperty = computed(() => {
  // Votre code qui dépend d'autres propriétés du composant
 }
)

Ecouter composant : calculée

<script setup>
 import { ref, computed } from 'vue'

 const value = ref('HelloWorld')
 const messageLength = computed(() => {
  return value.value.length
 })
</script>

<template>
 <input v-model="value" type="text"/>
 <p>{{ value }}</p>
 <p>Longueur {{ messageLength }}</p>
</template>

<style/>

Dossiers computedv3-sfc (version SFC) et computedv3 (version sans outil)

{{ value }}

Longueur : {{ messageLength }}

Ecouter composant : calculée

Possibilité de faire des mutateurs calculés (v-model)

<script setup>
 import { ref, computed } from 'vue'
 const value = ref('HelloWorld')
 const writableValue = computed({
  get() {
   return value.value;
  },
  set(newValue) {
   if (newValue.length <= 10) {
    value.value = newValue;
   }
  }
 })
</script>

<template>
 <input v-model="writableValue" />
 <p>{{ value }}</p>
</template>

Dossiers computedwritablev3-sfc (version SFC) et computedwritablev3 (version sans outil)

{{ value }}

Ecouter composant : observateur

Problématique

  • Invoquer une fonction lors d'un changement de valeur d'une propriété

Observateur (watch et watchEffect)

  • Cible une propriété (watch)
  • Cible plusieurs propriétés (watchEffect)

Syntaxe

watch(value, (newValue, oldValue) => {
 // Votre code
})

Ecouter composant : observateur

<script setup>
 import { ref, watch } from 'vue'

 const value = ref('')
 const message = ref('Parfait')

 watch(value, (newValue, oldValue) => {
  if (oldValue.length > 10) {
   message.value = "Trop Grand"
  } else {
   message.value = "Parfait"
  }
 })
</script>

<template>
 <input v-model="value" type="text" />
 <p>{{ message }}</p>
</template>
<style/>

Dossiers watchv3-sfc (version SFC) et watchv3 (version sans outil)

{{ message }}

Ecouter composant : observateur

<script setup>
 import { ref, watchEffect } from 'vue'

 const val1 = ref('')
 const val2 = ref('')
 const text = ref('')
 const stop = watchEffect(() => 
  text.value = val1.value + " " + val2.value
 )

 function stopWatch() { stop() }
</script>

<template>
  <div id="app">
    <input v-model="val1" type="text" />
    <input v-model="val2" type="text" />
    <p>{{ text }}</p>
    <button @click="stopWatch()">Stop</button>
  </div>
</template>

<style/>

Dossiers watcheffectv3-sfc (version SFC) et watcheffectv3 (version sans outil)

{{ text }}

Cycle de vie d'un composant

  • beforeCreate
  • created
  • beforeMount
  • mounted
  • beforeUpdate
  • updated
  • beforeUmount
  • Umounted

Cycle de vie d'un composant

Comment déclencher du code à certaines étapes ?

Utilisation de fonctions Hook (avant/après)

  • setup : création
  • onBeforeMount/onMounted : ajout dans le DOM
  • onBeforeUpdate/onUpdated : modification (propriété, DOM)
  • onBeforeUmount/onUmounted : desctruction
  • onErrorCaptured : capture des erreurs

Cycle de vie d'un composant

<script setup>
 import { createApp,ref,onBeforeMount... } from 'vue'

 display("before Create")
 const value = ref('HelloWorld')

 onBeforeMount(() => {
  display("Before Mount: " + value.value)
 })
 onMounted(() => {
  display("Mounted: " + value.value)
 })

 display("after Created: " + value.value)
</script>

<template>
  <div id="app">
    <input v-model="value" type="text" />
    <p>{{ value }}</p>
    <p>Afficher la console</p>
  </div>
</template>

<style/>

Dossiers lifecyclev3-sfc (version SFC) et lifecyclev3 (version sans outil)

{{ value }}

👇 Display the console

Résumé Contenu d'un composant

Différents contenus pour vos composants

  • propriétés : binding vue et modèle
  • computed : propriétés calculées
  • watch : propriétés observées
  • lifecycle : intercepteurs
  • fonctions : vos « fonctions » JavaScript
  • composants : composants externes
  • À suivre dans la partie communication : props

Communiquer entre composants

Parent ➡️ Enfant direct ?

Parent ➡️ Petit-enfant ?

Enfant ➡️ Parent ?

Parent ↔️ Enfant ?

Globalement 🌎 ?

En nommant explicitement 🫵 ?

Props

Permet une communication Parent ➡️ Enfant direct

Principe

  • Enfant : déclarer des Props
  • Parent : transmettre des valeurs depuis la balise personnalisée

Valeurs : statique et réactive

Props (Enfant)

<script setup>
 import { ref } from 'vue'
 const clock = ref(new Date().toLocaleTimeString())
  
 const props = defineProps({ message: String })

 setInterval(() => {
  computeClock();
 }, 1000);

 function computeClock() {
  clock.value = new Date().toLocaleTimeString();
 }
</script>

<template>
 <div>{{ clock }} {{ message }}</div>
</template>

<style/>

Dossiers propsv3-sfc (version SFC) et propsv3 (version sans outil)

Props (Parent)

<script setup>
 import { ref } from 'vue'
 import Clock from './components/Clock.vue'

 const value = ref("HelloWorld")
</script>

<template>
 <Clock :message="value"></Clock>
 <input v-model="value" type="text" />
 <p>{{ value }}</p>
</template>

<style/>

Dossiers propsv3-sfc (version SFC) et propsv3 (version sans outil)

{{ value }}

Provide/Inject

Permet une communication Parent ➡️ Petit-Enfant

Pourquoi ?

  • Éviter les props « passe-plat »

Principe

  • Parent : fournir une propriété
  • Enfants : injecter la propriété

Contrainte : hiérarchie obligatoire

Provide/Inject : Parent

<script setup>
 import { ref, provide } from 'vue'
 import Clock from './components/Clock.vue'

 const value = ref("HelloWorld")
 provide('message', value)
</script>

<template>
 <Clock></Clock>
 <div>
  <input v-model="value" type="text"/>
 </div>
</template>

Dossiers provideinjectv3-sfc (version SFC) et provideinjectv3 (sans outil)

Provide/Inject (enfants)

<script setup>
 import { ref } from 'vue'
 import ClockDisplay from './ClockDisplay.vue'
 const clock = ref(...)
 setInterval(() => {
  computeClock();
 }, 1000);
 function computeClock() { ... }
</script>
<template>
 {{ clock }} <ClockDisplay></ClockDisplay>
</template>

Composant Clock : dossier provideinjectv3-sfc (version SFC) et provideinjectv3 (sans outil)

<script setup>
 import { inject } from 'vue'
 const message = inject('message')
</script>
  
<template>
 {{ message }}
</template>

Composant ClockDisplay : dossier provideinjectv3-sfc (version SFC) et provideinjectv3 (sans outil)

Evénéments personnalisés

Permet une communication Enfant ➡️ Parent

Principe (gestion événement)

  • Enfant : émettre avec $emit
  • Parent : écouter via v-on ou @

Événement

  • nom d'identification
  • objet de transfert

Evénéments personnalisés (Enfant)

<script setup>
 import { ref } from 'vue'
 const clock = ref(new Date().toLocaleTimeString())
 defineEmits(['save-clock'])

 setInterval(() => {
  computeClock();
 }, 1000);

 function computeClock() {
  clock.value = new Date().toLocaleTimeString();
 }
</script>

<template>
 <button v-on:click="$emit('save-clock', clock)">
  {{clock}}
 </button>
</template>

<style/>

Dossiers customeventv3-sfc (version SFC) et customeventv3 (version sans outil)

Evénéments personnalisés (Parent)

<script setup>
 import { ref } from 'vue'
 import Clock from './components/Clock.vue'

 const value = ref("HelloWorld")

 function saveClock(currentClock) {
  value.value = currentClock;
 }
</script>

<template>
 <Clock @save-clock="saveClock($event)"/>
 <input v-model="value" type="text" />
 <p>{{ value }}</p>
</template>

<style/>

Dossiers customeventv3-sfc (version SFC) et customeventv3 (version sans outil)

{{value}}

V-Model pour composant

Permet une communication Parent ↔️ Enfant

Principe

  • Utilisation de v-model
  • Parent : passage de valeur en utilisant v-model
  • Enfant
    • Définir un Prop modelValue
    • Émettre un événement avec update:modelValue

V-Model pour composant

<script setup>
 import { ref } from "vue"
                
 defineProps({ modelValue })
 defineEmits(["update:modelValue"])
</script>
                
<template>
 <input :value="modelValue"
        @input="$emit('update:modelValue', 
                       $event.target.value)"/>
</template>

Composant FormInput customeventv3-sfc (version SFC) et customeventv3 (version sans outil)

<script setup>
 import { ref, reactive } from "vue"
 import FormInput from "./components/FormInput.vue"
                  
 const person = reactive({ name: "" })
</script>
                  
<template>
 <FormInput v-model="person.name"></Forminput>
 <p>{{ person.name }}</p>
</template>

Composant parent customeventv3-sfc (version SFC) et customeventv3 (version sans outil)

{{ person.name }}

V-Model multiple pour composant

Permet des communications Parent ↔️ Enfant

Principe

  • Utilisation de plusieurs v-model
  • Parent : passage de valeurs en utilisant v-model:prop_name
  • Enfant
    • Définir tous les Props
    • Émettre des événements avec update:prop_name

V-Model multiple pour composant

<script setup>
 import { ref } from "vue"
                
 defineProps({ name, city })
 defineEmits(["update:name", "update:city"])
</script>
                
<template>
 <input :value="name" @input="$emit('update:name',..."/>
 <input :value="city" @input="$emit('update:city',..."/>
</template>

Composant FormInput multiplecustomeventv3-sfc (version SFC) et multiplecustomeventv3 (version sans outil)

<script setup>
 import { ref, reactive } from "vue"
 import FormInput from "./components/FormInput.vue"
                  
 const person = reactive({ name: "", city: "" })
</script>
                  
<template>
 <FormInput 
    v-model:name="person.name" 
    v-model:city="person.city">
 </Forminput>
 <p>{{ person.name }} {{ person.city }}</p>
</template>

Composant parent multiplecustomeventv3-sfc (version SFC) et multiplecustomeventv3 (version sans outil)

{{ person.name }}

{{ person.city }}

Gestionnaire d'état

Permet une communication globale 🌎

Principe (solution DIY)

  • Créer un stockage
  • Importer le modèle de stockage
  • Utiliser des mutateurs calculés

Aller plus loin

  • Pinia (anciennement Vuex)

Gestionnaire d'état (stockage)

import { reactive } from 'vue'
const debug = true
const state = reactive({
 message: 'HelloWorld'
})

function setMessage(newValue) {
 if (debug) console.log('setMessage triggered with ', newValue)

 state.message = newValue
}

export function store() {
 return {
  state,
  setMessage
 }
}

Dossier statemanagerv3-sfc (version SFC) et statemanagerv3 (version sans outil)

Gestionnaire d'état (parent)

<script setup>
import { computed } from 'vue'

import Writable from './components/Writable.vue'
import Readable from './components/Readable.vue'
import { store } from './store.js'

const { state, setMessage } = store()

const message = computed({
 get() { return state.message },
 set(newValue) { setMessage(newValue) }
})
</script>

<template>
  <div><input v-model="message" type="text" />{{ message }}</div>
  <div><Writable></Writable><Readable></Readable></div>
</template>

<style/>

Dossier statemanagerv3-sfc (version SFC) et statemanagerv3 (version sans outil)

Gestionnaire d'état (enfants)

<script setup>
 import { store } from '../store.js'
 const { state } = store()
</script>

<template>
 {{ state.message }}
</template>

<style/>

Dossier statemanagerv3-sfc (version SFC) et statemanagerv3 (version sans outil)

<script setup>
 import { store } from '../store.js'
 const { state } = store()
</script>
   
<template>
 <input v-model="state.message" type="text" />
</template>
   
<style/>

Dossier statemanagerv3-sfc (version SFC) et statemanagerv3 (version sans outil)

{{ message }}

Référence directe

En nommant explicitement 🫵

Principe (parent ➡️ enfants)

  • Utiliser attribut ref
  • Déclarer propriété ref

Principe (enfant ➡️ parent)

  • Rechercher référence du parent

Problème

  • Couplage fort

Référence directe (parent)

<script setup>
 import { ref } from 'vue'
 import Child from './components/Child.vue'

 const refChild = ref(null)
 const value = ref("")

 function callParent(pValue) { value.value = pValue }

 defineExpose({
  callParent
 })
</script>

<template>
 <Child ref="refChild"></Child>
 <button @click="refChild.callChild()">Go</button>
 {{ value }}
</template>

<style/>

Dossier referencev3-sfc (version SFC) et referencev3 (version sans outil)

Référence directe (enfant)

<script setup>
 import { getCurrentInstance } from 'vue'
 const instance = getCurrentInstance()

 function callChild() { 
  instance.proxy.$parent.callParent("Hello..."); 
 }

 defineExpose({
  callChild
 })
</script>

<template>
 <div>Enfant</div>
</template>

<style/>
{{ value }}

Résumé

Technique Usage Avis
Props Parent ➡️ Enfant 👍
Provide/Inject Parent ➡️ Petit-Enfant 👍
Événement perso Enfant ➡️ Parent 👍
Gestionnaire états Globalité 🌎 👍
En nommant 🫵 Parent ↔️ Enfant 👎👎

Routage

Outils pour Vue.JS

Outils pour le développeur

Visual Studio Code

Intégration Vue.js

  • Vérification de la syntaxe
  • Formattage
  • Utilisation de snippets

Plugins : Volar et Vetur

Plugin ESLint

  • Vérification de la qualité de votre code Vue.js

Vue DevTools

Vue DevTools permet d'introspecter votre application Vue.js depuis le navigateur

Installation

Vite

Outil pour créer des projets Vue.js avec le format .vue

Création d'un projet (create-vue basé sur Vite)

$ npm create vite@latest

Installation des dépendances

$ npm install

Exécution pour tester

$ npm run dev

Exécution pour déployer

$ npm run build

Bibliothèques / Frameworks

VuePress : générateur de site statique

Pinia : gestionnaire d'états

Nuxt.js : framework du framework Vue.js

Vue Test Utils : tests

Bibliothèques de composants

Allez plus loin

Place aux travaux pratiques

Disponibles sur Github : github.com/mickaelbaron

Intégration dans un existant

Single Page Application (SPA)