Guillaume Rivière 2019 – 2024

Le logo de la CCI Bayonne Pays Basque

Développer des applications full web : devenir développeur full-stack

TP1 : Single-page application avec Vue.js

BUTS PÉDAGOGIQUES

  • Création de projets Vue.js
  • Création de composants Vue.js
  • Mise en place d'un routage monopage

L'objectif de ce sujet de TP est un tour d'horizon de Vue.js{Missing Image} pour prendre en main son fonctionnement et en comprendre les grands principes, au travers de sept exercices progressifs.

Exercice 1 • Créer et tester un nouveau projet Vue.js

Commençons par démarrer l'environnement de travail :

  1. Copiez sur votre disque dur les fichiers fournis par votre professeur :
    • La machine virtuelle Debian GNU/Linux {Missing Image} (Debian-11-Bullseye-64bits_Symfony5_Vue2) : tout l'écosystème logiciel nécessaire au développement y est installé ;
    • L'installateur de VirtualBox{Missing Image} (version 5.2.24).
  2. Pour être sûr que tout fonctionne correctement, nous avons besoin d'une version de VirtualBox ≥ 5.2.24. Si VirtualBox est déjà installé sur votre ordinateur, vérifiez la version : si inférieure à la version 5.2.24, désinstallez-là. Installez la présente version 5.2.24 de l'installateur.
  3. Ouvrez la machine virtuelle avec l'hyperviseur VirtualBox et démarrez la machine virtuelle.
  4. Ouvrez une session utilisateur avec les idenfiants indiqués par votre professeur (login et mot de passe).
  5. Ouvrez un terminal {Missing Image}

Créons un premier projet Vue.js{Missing Image} qui s'appellera « HelloVue » :

  1. Avec la commande suivante, créez dans votre répertoire personnel un nouveau répertoire appelé dev (c.-à-d., /home/estia/devel/) : ce répertoire contiendra tous les codes source :
    • mkdir devel
  2. Créez le nouveau projet HelloVue :
    • cd devel
    • vue create hello-vue
      Utilisez sur la touche [↓] pour choisir l'option Vue2 avec Babel et ESLint :
      {Missing Image}
      Puis appuyer sur la touche [Entrée].
      Patientez, car la création du projet peut prendre quelques instants (1 à 2 minutes) :
      {Missing Image}
      Ensuite, vérifiez le contenu du répertoire de projet fraîchement créé :
      • cd hello-vue
      • ls

Maintenant, observons l'exécution de ce nouveau projet Vue.js{Missing Image} qui est encore intact :

  1. Ouvrez un terminal {Missing Image} sur le bureau № 5 :
    {Missing Image}

    Astuce : utilisez les touches [Alt] + [←] | [→] | [↑] | [↓] pour naviguer rapidement entre les bureaux virtuels.

    Démarrez l'application Javascript avec le gestionnaire npm :
    • cd devel/hello-vue/
    • npm run serve
    Patientez pendant la construction du projet, jusqu'à ce que l'application soit en service :
    {Missing Image}
  2. Lancez l'exécution d'un navigateur web (Chromium{Missing Image} ou Firefox{Missing Image}) sur le bureau № 2, puis accédez à l'URL suivante :
    • http://127.0.0.1:8080/
    {Missing Image}

Observez le code source de la page web :

⇒ Nous voyons que le code HTML diffère de ce qui est affiché dans la page web. C'est le script app.js qui fait dynamiquement tout le travail de création des éléments en s'exécutant sur le navigateur web.

Inspectez ce qui est a été chargé dans le DOM HTML :

⇝ Dépliez les éléments en cliquant sur les pliures « ‣ » et retrouvez les balises HTML de ce qui est affiché dans la page web.

Exercice 2 • Afficher un message

Nous allons modifier le projet « HelloVue » pour observer sa structure et changer le contenu de la page web. Pour ce faire, revenez sur le bureau № 1.

  1. Les quatre fichiers qui nous intéressent sont les suivants :
    /home/estia/devel/
                     |-hello-vue/
                                |-public/
                                |       |-index.html
                                |-src/
                                     |-main.js
                                     |-App.vue
                                     |-components/
                                                 |-HelloWorld.vue
    1. Le fichier index.html est le fichier envoyé au client en intégrant le script app.js
    2. Le fichier main.js est le point d'entrée du script app.js
    3. Le fichier App.vue définit le composant Vue.js{Missing Image} utilisé par main.js
    4. Le fichier HelloWorld.vue définit le composant Vue.js{Missing Image} utilisé dans App.vue
  2. Ouvrez ces quatre fichiers dans un éditeur de texte ou dans un IDE.
    Par exemple, ouverture avec gedit depuis le terminal :
    • gedit public/index.html src/main.js src/App.vue src/components/HelloWorld.vue&
    Ou lancement depuis le menu : Applications > Accessoires > Éditeur de texte
    La fenêtre de gedit
    La fenêtre de gedit

    Si vous préférez, vous pouvez utiliser un éditeur de texte ou un IDE que vous connaissez (et maîtrisez) déjà parmi les suivants : Dans la suite, des indications seront données pour travailler avec gedit, qui est le plus basique de la liste.

  3. Apportons des modifications et observons les conséquences :
    • Dans le fichier App.vue :
      • Dans la section <template/>, modifiez le paramètre msg de la balise <HelloWorld/> pour renommer le message en « Bienvenue dans votre application Vue.js »
      • Enregistrez le fichier (Directement avec [Ctr] + [S] dans gedit)
      Allez sur le bureau № 2 et constatez le rafraîchissement automatique de la page de votre application web.
    • Dans le fichier App.vue :
      • Effacez la balise <img/> qui affiche le logo de Vue.js
      • Enregistrez le fichier
      Dans le fichier HelloWorld.vue :
      • Supprimez tout le contenu de la balise <div/> pour ne conserver que la balise <h1/> affichant la variable msg
      • Dans la section <style/>, ajoutez le bloc CSS permettant de colorer le texte des balises h1 en vert
      • Enregistrez le fichier
      Allez sur le bureau № 2 et vérifiez le résultat :
      {Missing Image}
  4. Et maintenant, mettons-nous à l'heure ! Continuons de modifier le fichier HelloWorld.vue :
    • Dans la section <template/>, en dessous du titre principal, ajoutez le paragraphe suivant qui affiche deux variables Vue.js :
      •     <p>Nous sommes le {{today}} et il est {{now}}</p>
    • Dans la section <script/>, ajoutez une virgule après l'accolade fermante du bloc props, puis ajoutez la définition des données qui déclarera et initialisera les deux variables Vue.js :
      •   data: function () {
            return {
              today: '--/--',
              now: '--:--'
            }
          }
    • Enregistrez le fichier
    Allez sur le bureau № 2 et observez le résultat :
    {Missing Image}
    Continuons :
    • Toujours dans la section <script/>, ajoutez une virgule après l'accolade fermante du bloc data, puis ajoutez la définition des méthodes qui déclarera la nouvelle méthode updateTime() qui actualisera le contenu des deux variables Vue.js chaque seconde :
      •   methods: {
            updateTime() {
              this.today = moment().format('dddd L')
              this.now = moment().format('LTS')
              setTimeout(this.updateTime, 1000)
            }
          },
      Puis, à la suite, ajoutez la définition des instructions à exécuter, au point de montage de notre composant <HelloWorld/> dans le DOM HTML, afin de lancer le premier appel de la méthode updateTime() :
      •   mounted: function () {
            moment.locale('fr')
            this.updateTime()
          }
    • Enregistrez le fichier
    Allez sur le bureau № 2 et observez le résultat :
    {Missing Image}
    ⇝ Les deux variables restent inchangées : ceci diffère évidemment du résultat espéré ! 😉
    ⇒ Regardons ce qu'il se passe du côté de la console du navigateur web :
    • Dans Firefox, faîtes la combinaison de touches : [Ctr] + [Maj] + [K]
    • Dans Chromium, faîtes la combinaison de touches : [Ctr] + [Maj] + [I], puis ouvrez l'onglet « Console »
    Si nécessaire, remontez avec l'ascenseur jusqu'aux premières erreurs :
    {Missing Image}
    ⇒ Nous voyons que la bibliothèque « moment », dont nous avons utilisé des fonctions pour récupérer l'heure (dans la fonction updateTime()), pose problème.
    Regardons aussi les messages dans le terminal du bureau № 5 : il y a effectivement des erreurs.
    ⇝ Solution : importer cette bibliothèque dans notre projet, tout simplement !
    • Tout au début la section <script/>, ajoutez la ligne suivante
      • import moment from 'moment'
    • Enregistrez le fichier
    Retournez sur le bureau № 2 et observez :
    {Missing Image}
    Vérifiez que les messages d'erreur dans le terminal du bureau № 5 ont disparu.

    Affichez de nouveau l'inspecteur de DOM HTML, depuis le navigateur web, et dépliez pour retrouver la balise de paragraphe qui affiche la date.

Exercice 3 • Créer de nouveaux composants Vue.js

Nous allons créer un composant « bouton compteur » qui compte à chaque fois que l'utilisateur clique dessus :

  1. Créons un nouveau fichier ButtonCounter.vue dans le répertoire « src/components/ » :
    /home/estia/devel/
                     |-hello-vue/
                                |-public/
                                |       |-index.html
                                |-src/
                                     |-main.js
                                     |-App.vue
                                     |-components/
                                                 |-ButtonCounter.vue       <= ici
                                                 |-HelloWorld.vue
    Avec gedit, par exemple, faîtes comme suit :
    • Ouvrez un nouvel onglet avec la combinaison  : [Ctr] + [T]
    • Choisissez le répertoire et donnez un nom à ce nouveau fichier avec la combinaison : [Ctr] + [S]
  2. Dans ce nouveau fichier :
    • Créez une nouvelle section <template/> à l'intérieur de laquelle vous allez insérer le code suivant :
        <button @click="increment">0</button>
    • Ensuite, créez une nouvelle section <script/> et définissez les propriétés Vue.js du composant :
      export default {
        name: 'ButtonCounter'
      }
    • Enregistrez le fichier
  3. Dans le fichier App.vue :
    • Dans la section <template/>, ajoutez la balise de notre nouveau composant en dessous de celle du composant HelloWorld :
        <ButtonCounter/>
    • Ensuite, dans la section <script/>, importez le composant au début :
      import ButtonCounter from './components/ButtonCounter.vue'
      puis, dans la propriété components, ajoutez une virgule apres HelloWorld et ajoutez ButtonCounter
    • Enregistrez le fichier
  4. Allez sur le bureau № 2 et observez l'apparition du bouton :
    Si rien n'apparait, redemandez la page au navigateur pour réinitialiser proprement.
    (car les appels de la fonction updateTime() peuvent gêner le rechargement de la page)
    {Missing Image}
  5. Dans le fichier ButtonCounter.vue :
    • Dans la section <template/>, modifiez la balise pour y afficher une variable appelée count :
        <button @click="increment">{{ count }}</button>
    • Ensuite, dans la section <script/>, ajoutez la définition des données qui déclarera et initialisera la variable Vue.js :
        data: function () {
          return {
            count: 0
          }
        },
      puis, ajoutez la définition des méthodes qui déclarera la nouvelle méthode increment() qui augmentera la valeur de la variable Vue.js à chaque appel :
        methods: {
          increment() {
            this.count++
          }
        }
    • Enregistrez le fichier
  6. Allez sur le bureau № 2 et cliquez sur le bouton :
    Si rien ne se passe, redemandez la page au navigateur pour réinitialiser proprement.
    {Missing Image} {Missing Image}

Notre bouton compteur fonctionne. Apportons maintenant une légère, mais utile, amélioration grâce aux « props » de Vue.js :

  1. Dans le fichier ButtonCounter.vue :
    • Dans la section <script/>, avant la définition des données, ajoutez la définition des props qui déclarera un nouveau paramètre appelé start :
        props: {
          start: {type: Number, default: 0}
        },
    • puis, ajoutez une virgule après l'accolade fermante du bloc methods, et ajoutez la définition des instructions à exécuter, au point de montage de notre composant <ButtonCounter/> dans le DOM HTML, afin d'initialiser la variable Vue.js du compteur :
        mounted: function () {
          this.count = this.start
        }
    • Enregistrez le fichier
  2. Dans le fichier App.vue :
    • Ajoutez deux nouveaux compteurs initialisés avec le « props » start :
          <ButtonCounter :start="100" />
          <ButtonCounter :start="1000" />
    • Enregistrez le fichier
  3. Allez sur le bureau № 2 et vérifiez que la valeur initiale est la bonne :
    Si tel n'est pas le cas, redemandez la page au navigateur pour réinitialiser proprement.
    {Missing Image} {Missing Image}

Notre bouton compteur est terminé. Profitons-en pour observer un mécanisme utile : pouvoir afficher des informations dans la console.

  1. Ouvrez le fichier package.json, situé dans le répertoire principal du projet, puis :
    • Dans la propriété eslintConfig, modifiez la sous-propriété rules en définissant l'option no-console comme suit :
          "rules": {
            "no-console": "off"
          },
    • Enregistrez le fichier
  2. Conséquemment au précédent changement du fichier package.json, redémarrons l'application Javascript. :
    • Depuis le terminal du bureau № 5, stoppez l'exécution avec la combinaison : [Ctr] + [C]
    • Relancez le service de l'application Javascript :
      • npm run serve
  3. Dans le fichier ButtonCounter.vue :
    • Dans la section <script/>, dans le code de la méthode increment(), ajoutez l'appel suivant après l'incrémentation :
            console.log('Le compteur vaut ' + this.count)
    • Enregistrez le fichier
  4. Allez sur le bureau № 2, affichez l'onglet "Console" des "Web Developper Tools" du navigateur web, puis cliquez sur les compteurs :
    Redemandez éventuellement la page au navigateur pour réinitialiser proprement.
    {Missing Image}

Afficher des informations dans la console est parfois utile, pour aider à comprendre ce qu'il se passe, en insérant des traces dans l'exécution du code.

Exercice 4 • Un autre composant Vue.js pouvant également décrémenter

Nous souhaitons maintenant créer un composant permettant d'incrémenter et de décrémenter une valeur. Ce nouveau composant s'appellera SpinButtonCounter.

Créez ce nouveau composant en vous inspirant du code de l'exercice précédent :

{Missing Image}

Essayez de le faire par vous-même, mais si jamais vous bloquez ou hésitez, vous pourrez cliquer pour apercevoir une solution pour le template de ce nouveau composant.

Exercice 5 • Ajouter de la navigation avec Vue Router

Nous allons maintenant mettre en place un principe fondamental des applications web monopage : le routage. Le routage va permettre de changer le composant chargé dans la page principale, ce qui permet de reproduire un comportement de navigation entre pages, mais sans demander de nouvelle page : c'est uniquement le contenu de la page qui va être rechargé.

  1. Effacez le contenu du fichier App.vue et remplacez par le code suivant qui contiendra la balise <router-view/> :
    <template>
      <div id="app">
        <router-view></router-view>
      </div>
    </template>
     
    <script>
    export default {
      name: 'app'
    }
    </script>
    Enregistrez le fichier
  2. Créez un nouveau fichier HelloPage1.vue dans le répertoire « src/components/ ». Ce composant contiendra uniquement un titre et un lien vers /cpt. Copiez le code suivant dans le fichier :
    <template>
      <div id="app1">
        <h1>Page 1</h1>
        <p><router-link to="/cpt">Compter</router-link></p>
      </div>
    </template>
     
    <script>
    export default {
      name: 'HelloPage1'
    }
    </script>
    Enregistrez le fichier
  3. Créez un nouveau fichier HelloPage2.vue dans le répertoire « src/components/ ». Ce composant contiendra uniquement un titre et un bouton compteur. Copiez le code suivant dans le fichier :
    <template>
      <div id="app2">
        <h1>Page 2</h1>
        <p><SpinButtonCounter/></p>
      </div>
    </template>
     
    <script>
    import SpinButtonCounter from './SpinButtonCounter.vue'
     
    export default {
      name: 'HelloPage2',
      components: {
        SpinButtonCounter
      }
    }
    </script>
    Enregistrez le fichier
  4. Mettons en place le routage dans le fichier principal src/router/index.js (créer avant le nouveau répertoire src/router/) :
    import VueRouter from "vue-router";
     
    const routes = [
      {
        path: "/",
        name: "HelloPage1",
        component: require('@/components/HelloPage1.vue').default
      },
      {
        path: "/cpt",
        name: "HelloPage2",
        component: require('@/components/HelloPage2.vue').default,
      },
      {
        path: "*",
        redirect: "/",
      },
    ];
     
    const router = new VueRouter({
      mode: "history",
      base: process.env.BASE_URL,
      routes,
    });
     
    export default router;
  5. Enregistrez le fichier
  6. Puis, dans le fichier main.js, ajouter les trois lignes suivantes :
    import VueRouter from 'vue-router'
    Vue.use(VueRouter)
    import router from './router'
    et l'insérer dans le composant principal en ajoutant router, avant la ligne render: ... dans la création de la vue.
  7. Enregistrez le fichier
  8. Allez sur le bureau № 2, affichez la console du navigateur web, puis cliquez sur le lien :
    {Missing Image} {Missing Image} Remarque : Utilisez la fonctionnalité « Page précédente » du navigateur web pour revenir en arrière
    Remarque : Essayez de demander directement la page http://127.0.0.1:8080/cpt au navigateur web : ça fonctionne !

Exercice 6 • Ajouter facilement et rapidement encore de nouveaux composants

En nous inspirant du bouton compteur ButtonCounter, nous allons créer un nouveau composant Vue.js de tirage aléatoire. Nous verrons que la fonction de tirage aléatoire Math.random() par défaut n'est pas très satisfaisante : nous en utiliserons alors une meilleure, provenant du paquetage externe seedrandom créé par David Bau.

  1. Créons un nouveau composant de « bouton aléatoire ». Le tirage sera effectué entre deux bornes min et max définies en paramètre du composant par ses propriétés « props ». Cette version utilisera Math.random(). Copiez le code suivant dans un nouveau fichier ButtonAlea.vue :
    <template>
      <button @click="randomize">{{ value }}</button>
    </template>
     
    <script>
    export default {
      name: 'ButtonAlea',
      props: {
          min: {type: Number, default: 0},
          max: {type: Number, default: 10}
      },
      data: function () {
        return {
          value: 0
        }
      },
      methods: {
        randomize() {
          this.value = Math.floor(Math.random() * (this.max - this.min + 1)) + this.min
         }
      },
      mounted: function () {
        this.randomize()
      }
    }
    </script>
  2. Créons un nouveau composant « HelloPage3 » qui définira trois boutons aléatoires, effectuant des tirages entre 1 et 6. Copiez le code suivant dans un nouveau fichier HelloPage3.vue :
    <template>
      <div id="app3">
        <h1>Page 3</h1>
        <p><ButtonAlea :min="1" :max="6" /></p>
        <p><ButtonAlea :min="1" :max="6" /></p>
        <p><ButtonAlea :min="1" :max="6" /></p>
      </div>
    </template>
     
    <script>
    import ButtonAlea from './ButtonAlea.vue'
     
    export default {
      name: 'HelloPage3',
      components: {
        ButtonAlea
      }
    }
    </script>
  3. Ajoutons un routage vers ce nouveau composant « HelloPage3 » dans le fichier src/router/index.js :
          path: '/des',
          component: require('@/components/HelloPage3.vue').default
  4. Ajoutons un lien vers /des depuis le composant « HelloPage1 » dans le fichier HelloPage1.vue :
        <p><router-link to="/des">Lancer les dés</router-link></p>
  5. Allez sur le bureau № 2, affichez la console du navigateur web, puis testez :
    {Missing Image} {Missing Image}

La génération aléatoire est plutôt insatisfaisante : les nombres se répètent souvent de manière consécutive.

Améliorons cela en utilisant une meilleure fonction de génération de nombres aléatoires de David Bau :

  1. Modifions le code du « bouton aléatoire » dans le fichier Alea.vue :
    • Au début de la section <script/>, ajoutez :
      var seedrandom = require('seedrandom');
      var rng = seedrandom();
    • Dans la méthode randomize(), remplacez Math.random() par rng() :
            this.value = Math.floor(rng() * (this.max - this.min + 1)) + this.min;
  2. Allez sur le bureau № 2, affichez la console du navigateur web, puis testez de nouveau.

Exercice 7 • Afficher des données dans Vue.js

Nous allons utiliser la directive Vue.js v-for pour afficher les éléments d'un tableau :

  1. Créons un nouveau composant « HelloPage4 », dans un nouveau fichier HelloPage4.vue, qui définira, dans les données (c.-à-d., un bloc data:) de sa section <script/>, le tableau suivant :
          species: [
            {
              "id": "1",
              "nom": "Roitelet huppé",
              "nbr": 0
            }, {
              "id": "2",
              "nom": "Bruant des roseaux",
              "nbr": 10
            }, {
              "id": "3",
              "nom": "Butor étoilé",
              "nbr": 15
            }, {
              "id": "4",
              "nom": "Héron pourpré",
              "nbr": 15
            }, {
              "id": "5",
              "nom": "Mésange noire", 
              "nbr": 5
            }, {
              "id": "6",
              "nom": "Moineau friquet",
              "nbr": 0
            }
          ]
    et qui affichera dans sa section <template/> le tableau suivant :
        <table>
          <tr><th>Espèce</th><th>Comptage</th></tr>
          <template v-for="specie in species">
            <tr v-bind:key="specie.id" ><td>{{ specie.nom }}</td><td><ButtonCounter :start="specie.nbr" /></td></tr>
          </template>
        </table>
  2. Pour pouvoir utiliser le composant ButtonCounter, ajoutez l'import de ce composant et ajoutez le aussi dans la liste des "components".
  3. Ajoutons un routage vers ce nouveau composant « HelloPage4 » dans le fichier src/router/index.js :
          path: '/birds',
          component: require('@/components/HelloPage4.vue').default
  4. Ajoutons un lien vers /birds depuis le composant « HelloPage1 » dans le fichier HelloPage1.vue :
        <p><router-link to="/birds">Compter les oiseaux</router-link></p>
  5. Allez sur le bureau № 2, affichez la console du navigateur web, puis testez :
    {Missing Image} {Missing Image}

Terminons en donnant un peu de style !

  1. Ajoutez à la fin du fichier HelloPage4.vue par exemple :
    <style>
    #app4 h1 {
      text-align: center;
    }
    #app4 table {
      margin: 0 auto;
      border: solid 2px #42B983;
      border-radius: 3px;
      text-align: center;
    }
    #app4 th {
      background-color: #42B983;
    }
    #app4 td {
      background-color: #F9F9F9;
    }
    #app4 th, #app4 td {
      min-with: 120px;
      padding: 10px 20px;
    }
    </style>
  2. Testez le résultat :
    {Missing Image}