Commentaires
Une des choses que nous devons voir est la liaison des composants à nos routes. C'est pas très clair, alors je vais essayer de reformuler :
Toute la question est la suivante : comment faire référence à nos routes dans nos composants ? C'est-à-dire, comment faire des liens de manière simple vers nos routes, et comment afficher le contenu d'une route.
En effet, posons-nous une question... Comment fonctionne vue router ?

Arthur, l'apprenti développeurC'est vrai ça, ça marche comment ?
En fait, vue-router s'apparente aux composants dynamiques. Au clic sur un lien qui mène vers une autre page, le composant dynamique devient le composant associé à la route. Mais en plus, vue-router gère la façon dont on passe d'un composant à un autre, gère l'URL, gère également des règles qu'on peut appliquer sur nos routes...
Vue-router fait beaucoup de choses, mais la base peut être assimilée aux composants dynamiques dont on a déjà parlés. Ça devrait un peu t'aider de savoir ça.

Arthur, l'apprenti développeurEn effet !
Bon, il est vrai que cela ne t'aide pas beaucoup à savoir comment nous allons pouvoir modifier les liens de la page d'accueil. Mais au point, ce point est désormais clair. Alors nous pouvons passer à la modification de ces liens :p.
Pour commencer, pour modifier ces fameux liens, il nous faut trouver le composant qui les affiche. Et ça, c'est fastoche, c'est dans App.vue.
Et son contenu est le suivant :

{"language":"text/html","content":"<template>\n <div id=\"nav\">\n <router-link to=\"/\">Home</router-link> |\n <router-link to=\"/about\">About</router-link>\n </div>\n <router-view/>\n</template>\n\n<style>\n#app {\n font-family: Avenir, Helvetica, Arial, sans-serif;\n -webkit-font-smoothing: antialiased;\n -moz-osx-font-smoothing: grayscale;\n text-align: center;\n color: #2c3e50;\n}\n\n#nav {\n padding: 30px;\n}\n\n#nav a {\n font-weight: bold;\n color: #2c3e50;\n}\n\n#nav a.router-link-exact-active {\n color: #42b983;\n}\n</style>\n","filename":"App.vue"}

Arthur, l'apprenti développeurMais qu'est-ce que c'est que <router-view> et <router-link> ?
C'est la façon d'interagir avec le router depuis un composant qu'on a créé ;). En fait, ce sont deux composants fournis par l'API de Vue Router. <router-link> nous permet de mettre un lien vers une route qu'on a créée, et <router-view> d'afficher le composant lié à la route sur laquelle on est actuellement :). Pour reprendre mon idée de composant dynamique, en fait, il est assez simple (et simpliste, je l'admets) de comparer le fait d'être sur une page de mon site, et d'avoir le bon composant de rendu au fait d'avoir différents onglets et, au clic sur l'un, d'avoir le bon composant associé d'affiché (fiou, tu as compris quelque chose ?).

Arthur, l'apprenti développeurJe relis, je relis... mais... Pas trop. Un exemple concret ?
Évidemment !
Prenons ce composant dynamique :

{"language":"text/html","content":"<button @click=\"currentTab = 'Home'\">Home</button>\n<button @click=\"currentTab = 'About'\">About</button>\n<component :is=\"currentTab\" v-bind=\"dynamicProps\"></component>","filename":""}

Hé bien, avec vue router, d'un côté la définition des routes (l'équivalent de "currentTab = 'Home'", "currentTab = 'About'") :

{"language":"text/javascript","content":"/** ... ***/\nconst routes = [\n {\n path: '/',\n name: 'Home',\n component: Home\n },\n {\n path: '/mentions-legales',\n name: 'Legals',\n component: Legals,\n },\n {\n path: '/connexion',\n name: 'Connexion',\n component: Connexion\n }\n]\n/** ... **/","filename":"router>index.js"}

Et de l'autre, le composant dynamique :

{"language":"text/html","content":"<div id=\"nav\">\n <router-link to=\"/\">Home</router-link> | <!-- Un peu l'équivalent d'un bouton -->\n <router-link to=\"/about\">About</router-link> <!-- Un peu l'équivalent d'un bouton -->\n</div>\n<router-view/> <!-- Un peu l'équivalent de <component is=\"...\" ... />","filename":"App.vue"}

Et voilààà. C'est plus clair ?

Arthur, l'apprenti développeurOui !
Attention cependant, comme je l'ai précisé, cet exemple est simpliste : il permet simplement de comprendre le fonctionnement de base de vue router ;).

Créer des liens entre les pages


Ok, bon, maintenant que tu as compris tout ça, nous pouvons modifier notre App.vue pour afficher les bons liens.

Arthur, l'apprenti développeurMais attends, il me manque encore des connaissances ! Comment bien utiliser <router-link> ? Je vois l'attribut to="...". Quel est l'intérêt d'avoir nommé nos routes si finalement on met le path dans l'attribut to="" ? Et y'a-t-il d'autres attributs utiles ?
Hé bien, en voilà une personne enjouée ! En tout cas, ta remarque est très pertinente. En effet, dans l'exemple généré par vue-cli, il est utilisé l'attribut to="", qui, comme tu l'as compris, indique le path de la route. Ce qui n'est pas le plus pratique, pour les raisons évoquées précédemment. Nous, nous allons toujours utiliser nos routes nommées. Pour ce, c'est toujours l'attribut to="" que nous allons utiliser, mais il va falloir lui spécifier un objet particulier. Il a cette forme :

{"language":"text/html","content":"<router-link :to=\"{name:'home'}\">Home</router-link>\n\n","filename":"App.vue"}

Tout est clair ?

Arthur, l'apprenti développeurParfaitement.
Du coup je te laisse afficher nos 3 routes sur la page d'accueil.

Arthur, l'apprenti développeurOk, je te fais ça !
Arthur, l'apprenti développeurEuh... Ça marche pas... Quand je clique sur un lien, j'ai aucun contenu qui s'affiche et l'URL est bizarre. Par exemple : http://localhost:8080/%7Bname:'Legals'%7D
Je me doutais que tu n'y ferais pas attention ! Réfléchissons quelques instants... Avant, on passait à l'attribut to="" une chaine de caractères. Maintenant on passe un objet. Si on ne veut pas que cet objet soit compris comme une chaine de caractères, il faut caster ! Et donc utiliser v-bind... Et regarde, c'est ce que j'avais fait juste au dessus, j'avais rajouté les deux-points (:). Allez, regarde la solution :

{"language":"text/html","content":"<template>\n <div id=\"nav\">\n <router-link :to=\"{name:'Home'}\">Home</router-link> |\n <router-link :to=\"{name:'Legals'}\">Mentions légales</router-link> |\n <router-link :to=\"{name:'Connexion'}\">Connexion</router-link>\n </div>\n <router-view/>\n</template>\n\n<!-- Style... -->","filename":"App.vue"}

Arthur, l'apprenti développeurOk merci, c'est réglé. Mais j'ai encore une question. Il se passe quoi quand je mets un router-link ? Qu'est-ce qu'il y a comme balise derrière ?
Encore une excellente question. Si tu inspectes les liens dans ton navigateur, tu verras qu'il s'agit de... liens. Donc de balises a. Avec des classes particulières selon si le lien est actif ou non.

Arthur, l'apprenti développeurOk, mais du coup est-ce que je peux changer ça ? Par exemple, j'aimerais bien rajouter une petite icône devant le lien "Home"...
C'est possible. En fait, pour ce, il faut utiliser le principe des slots. Attention, il faut être calé sur ce sujet, car il s'agit de scoped slots ! Et également utiliser un attribut nommé "custom", mis à disposition par vue router.
Du coup, prenons le lien "Home" et précisons le contenu que nous souhaitons pour ce lien :
{"language":"text/html","content":"<template>\n <div id=\"nav\">\n <router-link :to=\"{name:'Home'}\" custom v-slot=\"{ navigate, href }\">\n <a :href=\"href\" @click=\"navigate\"><span class=\"material-icons\">home</span>Home</a>\n </router-link> |\n <router-link :to=\"{name:'Legals'}\">Mentions légales</router-link> |\n <router-link :to=\"{name:'Connexion'}\">Connexion</router-link>\n </div>\n <router-view/>\n</template>\n\n<!-- Style... -->","filename":"App.vue"}

Pour information, on utilise v-slot car nous sommes dans un cas de scoped slot (besoin d'un petit rafraichissement sur les slots : cours sur les slots). Navigate est une fonction qui gère toute la partie de changement de page par vue router et bloque les évènements quand c'est nécessaire, et href est l'URL de la page ;).

Arthur, l'apprenti développeurFiou c'est compliqué, mais au moins j'ai mon icône de maison :D

Les routes imbriquées (nested routes)


Vu notre lancée, il serait triste de nous arrêter là... Et donc on va continuer avec les routes qu'on n'avait pas créées à la partie précédente.
Commençons par ces 2 routes-ci :
* Page d'accueil avec résumé des dernières actualités
* Page pour voir une actualité en particulier et ses commentaires
Nous pouvons imaginer que la racine de l'URL sera la même pour ces 2 routes, et qu'il y aura un design commun aux 2. Ainsi, nous pouvons utiliser les routes imbriquées. Cela signifie que nous pouvons dire à Vue.JS : tiens, crée-moi une route "parente" qui aura tel composant. Crée-moi ensuite 2 enfants qui complèteront l'URL de base et qui s'afficheront comme composant enfant dans le composant du parent.
Pour être plus clair, voici ce que nous allons faire :

  • Page d'accueil avec résumé des dernières actualités : localhost/news

  • Page pour voir une actualité en particulier et ses commentaires : localhost/news/id_de_la_news


Nous voyons bien qu'il y a une partie commune aux 2 url : localhost/news. Définissons alors une première route correspondant à cela :
{"language":"text/javascript","content":"import { createRouter, createWebHistory } from 'vue-router'\nconst Home = () => import('../views/Home')\nconst Legals = () => import('../views/Legals')\nconst Connexion = () => import('../views/Connexion')\nconst News = () => import('../views/News')\n\nconst routes = [\n {\n path: '/',\n name: 'Home',\n component: Home\n },\n {\n path: '/mentions-legales',\n name: 'Legals',\n component: Legals,\n },\n {\n path: '/connexion',\n name: 'Connexion',\n component: Connexion\n },\n {\n path: '/news',\n name: 'News',\n component: News,\n }\n]\n\nconst router = createRouter({\n history: createWebHistory(process.env.BASE_URL),\n routes\n})\n\nexport default router\n","filename":"router/index.js"}


Et maintenant, créons nos 2 enfants. Pour cela, on va utiliser l'attribut "children" :
{"language":"text/javascript","content":"import { createRouter, createWebHistory } from 'vue-router'\nconst Home = () => import('../views/Home')\nconst Legals = () => import('../views/Legals')\nconst Connexion = () => import('../views/Connexion')\nconst News = () => import('../views/News')\nconst NewsAll = () => import('../views/NewsAll')\nconst NewsShow = () => import('../views/NewsShow')\n\nconst routes = [\n {\n path: '/',\n name: 'Home',\n component: Home\n },\n {\n path: '/mentions-legales',\n name: 'Legals',\n component: Legals,\n },\n {\n path: '/connexion',\n name: 'Connexion',\n component: Connexion\n },\n {\n path: '/news',\n name: 'News',\n component: News,\n children: [\n {\n path: '',\n name: 'News.All',\n component: NewsAll,\n },\n {\n path: ':id(\\\\d+)',\n name: 'News.Show',\n component: NewsShow\n }\n ]\n }\n]\n\nconst router = createRouter({\n history: createWebHistory(process.env.BASE_URL),\n routes\n})\n\nexport default router\n","filename":"router/index.js"}

Arthur, l'apprenti développeurOula, je ne suis pas sûr de tout comprendre... C'est quoi :id(\\d+) ? Et pourquoi tu as donné comme noms News.Quelquechose ? Et c'est bien beau tes routes imbriquées, mais qu'est-ce que cela implique côté composants et views ?
Excellentes remarques Arthur ;).
D'abord, pour le nom : sache que c'est juste une question d'organisation. Quand je vais appeler mes routes, comme ça, je saurai qu'il s'agit de routes imbriquées dépendant de News.
Concernant le :id(\\d+) c'est un attribut de la route. C'est-à-dire, une donnée qui peut varier dans l'URL mais qui sera récupérée sous le nom d'id dans notre cas. Par exemple, on pourra avoir localhost/news/1, localhost/news/12, localhost/news/124343053 etc. Mais aussi localhost/news/du_texte. Or, nous, on ne veut pas de texte ! On veut un id, qui est un entier. C'est pour cela qu'il y a (\\d+) : il s'agit d'une expression régulière (regex) qui oblige à ce que l'id soit un entier d'au moins 1 nombre. Pas très grave si tu ne comprends pas, sache-juste que ce petit bout de code barbare nous évite que id soit une chaine de caractères ou je ne sais quoi d'autre ;).

Arthur, l'apprenti développeurOk, ça marche. Et pour les composants du coup ?
L'avantage des nested routes est que nous pouvons utiliser le composant de la route "parente" pour avoir des éléments de design commun entre les enfants !
Commençons par coder les éléments communs (franchement, rien de bien terrible, c'est vraiment pour la démonstration, je n'étais pas très inspiré...). Comme vous le remarquerez, j'utilise TailwindCSS comme à l'habituel :

{"language":"text/html","content":"<template>\n <div class=\"bg-gray-50 pt-8 pb-8 px-8 max-w-7xl mx-auto\">\n <header class=\"mx-auto divide-y-2 divide-gray-200\">\n <h1 class=\"text-3xl tracking-tight font-extrabold text-gray-900\">\n Les actualités\n </h1>\n </header>\n <main class=\"mt-12 grid gap-16 pt-12 grid-cols-3 gap-x-5 gap-y-12\">\n <!-- Ici, on met l'affichage qu'on veut : soit toutes les actualités, soit une actualité et ses commentaires -->\n </main>\n </div>\n \n</template>","filename":"News.vue"}

Et voilà ce qui t'intéresse... Comment on peut afficher deux choses différentes selon l'URL dans notre composant... Tu as déjà la réponse, on l'a déjà fait :). Comment a-t-on fait dans App.vue pour afficher chacun de nos composants en fonction de la route ?

Arthur, l'apprenti développeurLa balise <router-view> ?
C'est exact. Et là, le monde va te paraitre infini. Car si tu as bien suivi, cela signifie qu'on peut mettre des router-view dans des router-view dans des router-view et ainsi de suite :). En fait, dès qu'on utilise des routes imbriquées !

{"language":"text/html","content":"<template>\n <div class=\"bg-gray-50 pt-8 pb-8 px-8 max-w-7xl mx-auto\">\n <header class=\"mx-auto divide-y-2 divide-gray-200\">\n <h1 class=\"text-3xl tracking-tight font-extrabold text-gray-900\">\n Les actualités\n </h1>\n </header>\n <main class=\"mt-12 grid gap-16 pt-12 grid-cols-3 gap-x-5 gap-y-12\">\n <router-view />\n </main>\n </div>\n \n</template>","filename":"News.vue"}

Selon l'URL (soit /news, soit /news/un_nombre), on va avoir une page différente ! Pratique non ?

Arthur, l'apprenti développeurWouaaahou.
On n'a pas fini toutes nos routes. Pour le moment, j'aimerais qu'on s'occupe de nos 2 nouveaux composants NewsAll.vue et NewsShow.vue ;).
Déjà, modifions App.vue pour pouvoir afficher toutes nos actualités :

{"language":"text/html","content":"<template>\n <div id=\"nav\">\n <router-link :to=\"{name:'Home'}\" custom v-slot=\"{ navigate, href }\">\n <a :href=\"href\" @click=\"navigate\"><span class=\"material-icons\">home</span>Home</a>\n </router-link> |\n <router-link :to=\"{name:'News.All'}\">Consulter les actualités</router-link> |\n <router-link :to=\"{name:'Legals'}\">Mentions légales</router-link> |\n <router-link :to=\"{name:'Connexion'}\">Connexion</router-link>\n </div>\n <router-view/>\n</template>","filename":"App.vue"}

Parfait ! Du coup, nous allons maintenant pouvoir nous attarder à la récupération des actualités via l'API de Mediastack. Nous allons voir qu'il y a plusieurs façon de consommer une API.
Soit, comme d'habitude, dans le created du composant. Soit, en "prefetching", c'est-à-dire, avant même que le composant ne soit appelé ;). Magique ! J'ai terminé cette partie
Demander de l'assistance