Développement d'applications mobiles

Partie 2 - Vue.js et Progressive Webapps

Cours 3 - Client-side routing



Alex morel
Code Lutin

Plan du cours

Cours 1: introduction à Vue.js

Cours 2: Style (Bulma & class-binding)

Cours 3: Client-side routing

Cours 4: Rest, promises, ouverture

Résumé des deux premiers cours

On est maintenant capable de

  • Créer une application vue avec Vue CLI
  • Définir des composants avec leurs props/data
  • Utiliser v-bind, v-if, v-on, v-for
  • Mettre en place un framework css (Bulma) et l'utiliser
  • Faire du binding de style et de classes CSS

Ce qui nous manque

Qu'est ce qu'il manque à notre application pour être montrable ?

☰ Un menu et une structure en différentes pages

C'est l'objectif de notre séance

6. Routing et composants

Dans toutes vos applications web, vous aurez besoin de servir plusieurs pages, toutes accessibles à une URL différente

C'est ce qu'on appelle le routing. Nous allons découvrir les mécaniques proposés par Vue pour mettre en place le routing de notre application.

Premièrement, ajoutons un menu à notre application. Dans l'esprit de Vue, votre application va être fait de pleins de petits blocs réutilisables appelés composants.

Pour l'instant, on a un composant App pour l'application et un composant Composter pour afficher un composteur.

Crééons un nouveau composant MenuView

Crééons un nouveau composant MenuView

  • Créer un fichier MenuView.vue dans le dossier components
  • Dans le fichier MenuView.vue, déclarer un template et un script minimal :
    <template>
       

    MENU

    </template>
  • Dans le composant App (App.vue), importer MenuView :
  • Ajouter la MenuView en haut du template du composant App
    <template>
      <div>
        <MenuView />

Exercice 12: Menu

  • Ajouter un menu à l'application en utilisant les navbar Bulma
  • Le menu doit avoir l'apparence suivante:
  • (facultatif) : sur mobile, définir un burger qui ouvre/ferme le menu

Voir commit Exercice 12 pour un exemple de menu responsive animé

On utilise notamment le @click pour modifier une data mobileMenuActive qui conditionne l'affichage du menu mobile.

Exercice 13: création de composants

  • Créer un composant AboutPage avec une prop "message" et l'afficher au-dessus de la liste de composteurs
  • Créer un composant CompostersList et l'utilisater dans App.vue
  • App.vue doit maintenant ressembler à ceci :

Grace aux 2 précédents exercices, on a maintenant un menu et 2 composants séparés (AboutPage et CompostersList)

Je voudrais que mon menu me permette d'afficher l'AboutPage ou la CompostersList dans la div container

Je voudrais que mon menu me permette d'afficher l'AboutPage ou la CompostersList dans la div container

Je voudrais également que l'url de mon app change en fonction du contenu affiché (e.g. /about et /composters)

C'est ce que permet de faire le router Vue

C'est ce que permet de faire le router Vue

Le router vue permet de faire du Client-side Routing (contrairement à la traditionnelle approche du Server-side routing)

Dès que l'on clique sur un lien, une requête est envoyée au serveur et c'est le serveur qui retourne la page à charger

On parle de Server-side Routing parce que le navigateur effectue une requête vers le serveur dès que l'URL change.

Le Server-side Routing a plusieurs inconvénients (mais aussi ses avantages)

  • Consommation bande passante: on télécharge systématiquement toute la page (même si il y a du code en commun)
  • Pas de mode offline: mettre les pages en cache ne fonctionnnera que si on a déjà visité toutes les pages
  • Davantage de travail pour le serveur: ce qui peut aussi être une bonne chose (moins de travail pour les vieux navigateurs)
  • Pas d'optimisation de rendu: si 80% de la page reste identique, on pourra imaginer ne changer dans le DOM que ce qui change effectivement

C'est pour ces raisons que l'on a mis au point le Client-side routing

Ici le serveur ne va renvoyer qu'une seule fois l'index.html qui inclue tout le code de notre application Vue

Puisque la navigateur a accès à tout le code, quand on change d'URL le Router vue va juste calculer les différences à appliquer sur le DOM, sans faire appel au serveur ni recharger la page.

Server-side routing

Server-side routing

Client-side routing

Single Page Applications (SPA)

Une SPA est une application web qui:

  • Charge une page unique (ici index.html)
  • Met à jour dynamiquement la page quand l'utilisateur interragit avec l'application

Lors du premier cours, on avait déjà étudié l'index.html

Lors du premier cours, on avait déjà étudié l'index.html

<html lang="en">
	<head>
		<meta http-equiv="X-UA-Compatible" content="IE=edge">
		<meta name="viewport" 
			content="width=device-width,initial-scale=1.0">
		<title><%= htmlWebpackPlugin.options.title %></title>
	</head>
	<body>
		<noscript>
			<strong>We're sorry but 
			<%= htmlWebpackPlugin.options.title %> 
			doesn't work properly without JavaScript enabled.
			Please enable it to continue.</strong>
		</noscript>
		<div id="app"></div>
		<!-- built files will be auto injected -->
	</body>
</html>

Lors du premier cours, on avait déjà étudié l'index.html

index.html est donc la "single page" de notre Single page Application, dans laquelle tout le code de l'application est monté.

Le router View va se servir de cette page pour effectuer le client-side routing

Étape 1: installer la librarie vue-router

npm install vue-router@4
  • Installe la version 4 ou plus de vue-router
  • Met à jour le fichier package.json

Étape 2: définir nos routes

  • Créer le fichier src/router/index.js
  • Importer nos composants et définir un tableau de routes
import CompostersList from '../components/CompostersList.vue'
import AboutPage from '../components/AboutPage.vue'

const routes = [
  {
    path: '/composters',
    name: 'CompostersList',
    component: CompostersList
  },
  {
    path: '/about',
    name: 'About',
    component: AboutPage
  }
]

Étape 2: définir nos routes

import CompostersList from '../components/CompostersList.vue'
import AboutPage from '../components/AboutPage.vue'

const routes = [
  {
    path: '/composters',
    name: 'CompostersList',
    component: CompostersList
  },
  {
    path: '/about',
    name: 'About',
    component: AboutPage
  }
]

Chaque route est définie avec un path (son URL), un name et le component à router

Étape 3: créer le router

import { createRouter, createWebHistory } from 'vue-router'
import CompostersList from '../components/CompostersList.vue'
import AboutPage from '../components/AboutPage.vue'

const routes = [...]

const router = createRouter({
  history: createWebHistory(process.env.BASE_URL),
  routes
})
export default router

La méthode createWebHistory permet d'éviter d'avoir des hash dans l'url (voir l'explciation détaillée ici)

Étape 4: brancher le router à l'application

Dans le fichier main.js qui créé et injecte l'application Vue, brancher le router

import { createApp } from 'vue'
import App from './App.vue'
import router from './router'

createApp(App)
    .use(router)
    .mount('#app')

Étape 5: définir dans quel div le routeur doit rendre le contenu

Dans le fichier App.vue, remplaçons le contenu actuel par:

Exercice 14: un peu de routing

  • En suivant les étapes présentées, brancher le router à notre application
  • Tester via le Menu les URLs /composters, /about, / ?
  • Définir un composant HomePage branché sur l'url /
  • Ouvrir la vue "Network"(Réseau) de votre navigateur et cliquer sur les liens. Constatez-vous un problème par rapport à ce que le router est censé faire ?

Notre routing fonctionne mais...

La page semble se recharger intégralement quand on clique sur nos liens

Je voudrais indiquer à Vue qu'il n'est pas nécessaire que le navigateur recharge toute la page

Je voudrais indiquer à Vue qu'il n'est pas nécessaire que le navigateur recharge toute la page

C'est l'intérêt du composant router-link (fourni par la librairie vue-router)


	Accueil

	Accueil

	Accueil
 

Le composant router-link dispose de nombreuses props (voir la liste complète ici)

Il existe de nombreuse façon de définir la route: via une URL, un nom, en ajoutant des paramètres...


	A propos

Si la route courante correspond au lien to d'un router-link, ce dernier se voit attribué par défaut la classe css .router-link-active

Exercice 15: no more requests

  • Réecrire la MenuView pour utiliser des router-link là où ça vous semble pertinent
  • Ouvrez la vue "Network"(Réseau) de votre navigateur et cliquez sur les liens, que constatez-vous ?
  • Faire en sorte que le lien correspondant à la page courante soit affiché en gras et souligné en vert

Vous serrez probablement amenés à devoir définir des URLs contenant des paramètres (e.g. /events/ID DE MON EVENT)

Il est possible de définir des paramètres dans une route :

{
	path: '/events/:eventId',
	name: 'EventDetails',
	props: true,
	component: EventDetails
}
{
	path: '/events/:eventId',
	name: 'EventDetails',
	props: true,
	component: EventDetails
}

Ces paramètres seront automatiquement mappés aux props du composant:

export default {
  name: 'EventDetails',
  props: {
	  eventId: String,
  },
}
{
	path: '/events/:eventId',
	name: 'EventDetails',
	props: true,
	component: EventDetails
}
export default {
  name: 'EventDetails',
  props: {
	  eventId: String,
  },
}

Il est toujours possible d'utiliser des router-links :


	Détails de l'évènement {event.name}

Exercice 16: routing avec paramètres

  • Créer un composant ComposterDetailPage.vue qui se contente d'afficher un titre "Détail du composteur {{ id }}" puis réutilise le composant Composter
  • Définir une route /composter-detail/:id associée à cette nouvelle page
  • Ajouter une entrée dans le menu permettant d'afficher la page de détails du composteur 42

Note: il est possible d'exécuter un changement de route en JS pur (tous les détails ici)

// literal string path
router.push('home')

// object
router.push({ path: 'home' })

// named route
router.push({ name: 'user', params: { userId: '123' } })

// with query, resulting in /register?plan=private
router.push({ path: 'register', query: { plan: 'private' } })

Les SPA permettent généralement de réduire le trafic réseau puisque le code est téléchargé une seule fois. Mais il est possible que ce comportement ne soit pas optimal. Avez-vous un exemple ?

→ Si notre app a une page d'accueil simple visitée par 100k visiteurs par jours, et que le reste de l'application est reservée aux membres loggés (1k visite par jour), il semble peu judicieux que les 100k qui arrivent sur la page d'accueil téléchargent toute l'application.

→ le router vue permet de préciser qu'une ou plusieurs routes doivent faire l'objet d'une SPA dédiée

→ le router vue permet de préciser qu'une ou plusieurs routes doivent faire l'objet d'une SPA dédiée

{
	path: "/",
	name: "LightHome",
	// route level code-splitting
	// this generates a separate chunk (ligh-home.[hash].js) for this route
	// which is lazy-loaded when the route is visited.
	component: () => import(/* webpackChunkName: "light-home" */ "../pages/LightHome.vue"),
}

C'est un usage avancé, mais prenez le réflexe de penser à faire de l'Eco-conception

C'est un usage avancé, mais prenez le réflexe de penser à faire de l'Eco-conception

Ce n'est pas le sujet du cours, mais je vous invite à vous renseigner sur l'Eco-Index (des plugins pour Chrome/Firefox permettent de le calculer facilement) et l'eco-conception en général (par exemple en regardant cette conférence de mes collègues lutins)

Le router vue permet des usages bien plus avancés (e.g. ajouter des conditions de gardes à certaines routes, définir des routes imbriquées...)

La documentation de Vue est vraiment très bien faite, n'hésitez pas à la consulter.

Point d'avancement

On est maintenant capable de

  • Créer une application vue avec Vue CLI
  • Définir des composants avec leurs props/data
  • Utiliser v-bind, v-if, v-on, v-for
  • Mettre en place un framework css (Bulma) et l'utiliser
  • Définir des computed properties pour factoriser des calculs
  • Faire du binding de style et de classes CSS
  • Comprendre ce qu'est le Client-side routing (succès de conversation idéal pour se faire des amis)
  • Mettre en place et configurer un router vue sur des cas d'usages simples

Ce qui nous manque

Qu'est ce qu'il manque à notre application pour être utile ?

🌐 Aller chercher des vraies données depuis un serveur REST

⌚ Gérer l'asynchronicité (Promise)

✏ Comprendre le cycle de vue des composants Vue

Ce sera l'objectif de notre prochaine (et dernière) séance


D'ici le prochain cours

- Entraînez-vous

- Harcelez-moi (par mail et/ou sur discoord)



Alex morel
Code Lutin