Vue.js : Rendre son app 50% plus légère en migrant de Vue 2 à Vue 3

L’équipe VueJS a sorti une nouvelle version de Vue.js en Septembre 2020. Cette nouvelle version apporte de nombreuses fonctionnalités, optimisations mais également son lot de changements importants.

En Juin 2021, l’équipe Vue.js a sorti une nouvelle version : 3.1. Cette nouveauté apporte la compatibilité @vue/compat, permettant de migrer simplement les gros projets. Cette compatibilité est importante car elle nous permet de faire fonctionner Vue 2 au côté du code Vue 3.

Migrer vers Vue 3 représentait un travail important pour nous chez Crisp (Environ 250 000 lignes de codes Vue).

Nous sommes donc passés de Vue 2.6 à Vue 3.2. C’est un travail que nous avons réalisé en 2 semaines à temps pleins pour deux personnes.

Nous avons préparé cette migration en lisant le guide officiel de migration Vue. Bien évidemment, tout ne s’est pas passé comme prévu et nous avons rencontré de nombreux imprévus…

Vous l’avez compris, cet article est le résultat de notre processus de migration entre Vue 2 et Vue 3. L’idée, c’est de partager nos découvertes pour que vous ne fassiez pas les mêmes 💪🏼

Qu’est-ce que Vue.js ?

Créé en 2013, la philosophie initiale de Vuejs était de proposer une alternative minimaliste à Angular 1.

À cette époque, Angular était framework conséquent offrant de nombreuses fonctionnalités innovantes.

Les premières versions de Vue ressemblaient à des framework Angular minimalistes offrant des templates, du data bindings, des filtres et des directives.

Vue2 a été mis en ligne en 2016 (à peu près en même temps que Angular 2) et s’est naturellement positionné comme une alternative performante à Angular.

En fait, de nombreux développeurs utilisant Angular 1 ont perçu en Vue 2 un grand intérêt car Angular 2 ne tenait pas toutes ses promesses. Concrètement, Vue 2 offrait quelque chose d’aussi simple que AngularJS mais avec de meilleures performances.

Vue 3 a été créé pour améliorer les performances du framework. Honnêtement, c’est plus une évolution qu’une révolution. D’ailleurs la plupart des nouvelles fonctionnalités et des changements importants ont été apportés pour améliorer la performance de Vue.js.

Le moteur de réactivité interne a été reconstruit de zéro, ce qui a engendré la fin du support de IE11 (qui n’est pas une grande perte 😂).

Finalement, cette nouvelle version est destinée à être plus modulable et introduit des fonctionnalités comme « Composition API« . Voici quelques changements important dans la logique d’un passage Vue 2 à Vue 3.

Changements Important sur Vue.js 3

Quelques informations importantes qui nous ont été importantes de connaître et de comprendre avant de nous lancer dans la migration.

Vue.js Global API

L’API Vue global a été déprécié. Malgré le fait que ce soit toujours supporté via @vue/compat, refondre votre app sera nécessaire pour supporter Vue 3 au maximum.

Cela signifie que ce ne sera pas possible d’utiliser les APIs tel que Vue.set ou Vue.delete. Comme Vue 3 offre désormais un nouveau moteur de réactivité, utiliser ces APIs est inutile !

Au lieu d’utiliser  vue.set(object, key, value) vous pouvez directement utiliser object[key] = value

Filtres

Comme expliqué précédemment, Vue a été créé comme une alternative à Angular. C’est pour cela que les filtres ont été supportés dès le début.

Le plus gros problème avec les filtres c’est la performance : la fonction de filtre doit être exécutée à chaque fois que la donnée est mise à jour. C’est pour cela que les filtres ont été dépréciés.

Il n’est donc plus possible d’utiliser les commandes suivantes {{ lastName | uppercase }}. Au lieu, vous allez devoir utiliser des propriétés spécifiques type {{ upercasedLastName }} ou des méthodes comme {{ uppercase (lastName)}}.

Instanciation de Vue.js et des Plugins

Avec Vue 3, vos applications et plug-ins ne sont plus instanciés de façons communes. Cela signifie que vous pouvez avoir plusieurs applications Vue dans le même projet.

Exemple d’instanciation sous Vue 2:
import Vue from "vue";
new Vue({
router,
render: h => h(App)
}).$mount("#app");

Exemple d’instanciation sous Vue 3:
import { createApp, h } from "vue";
const app = createApp({
render: () => h(App)
});
app.use(router);
app.mount("#app");

v-if + v-for

Utiliser les conditions v-if avec les listes v-for était monnaie courante sous Vue 2. Pour des questions de performance, ce comportement a été désactivé.

Depuis Vue 3, vous devez utiliser des propriétés de listes calculées.

v-model

L’API v-model a beaucoup changé sur Vue 3.

La propriété value a été remplacée par modelValue

<ChildComponent v-model="pageTitle" />

ChildComponent doit ainsi être réécrit de la façon suivante:

props: {
modelValue: String // previously was `value: String`
},
emits: ['update:modelValue'],
methods: {
changePageTitle(title) {
this.$emit('update:modelValue', title)
}
}

Le truc cool, c’est que c’est désormais possible d’avoir plusieurs valeurs v-model personnalisées, accompagnées de v-model:valueA et v-model:valueB

$emit

Depuis Vue 3, ce n’est plus possible d’envoyer des événements globaux comme EventBus. Très simplement, vous ne pouvez plus utiliser vm.$on ni vm.$off.

Pour le remplacer, nous vous suggérons d’utiliser cette librairie du Mitt.

mounted() {
  this.eventbus = mitt();
  
  eventbus.on("ready", () = {
    console.log("Event received");
  });

  eventbus.emit("ready");
}

Pour vous donner une idée, le même code sous Vue 2 serait le suivant:

mounted() {  
  this.$on("ready", () = {
    console.log("Event received");
  });

  this.$emit("ready");
}

C’est toujours possible d’émettre des événements depuis des composants vers leurs parents. Cependant, tous les événements devront être déclarés en amont.

Par exemple, si votre composant possède une propriété @click, émît en utilisant this.$emit("click"), vous devez déclarer l’événement "click" dans votre composant comme présenté ci-dessous:

props: {
  name: {
    type: String,
    default: ""
  },
},

emits: ["click"], // events have to be declared here

data() {
  return {
    value: ""
  }
}

 

Notre stratégie de migration pour Crisp

Notre application s’appelle Crisp. C’est une solution de support client tout-en-un à destination des TPE et des PME partout dans le monde.

Chaque jour, c’est 300 000 entreprises qui utilisent nos services pour répondre à leurs clients, sur de nombreux canaux (Email, Messenger, WhatsApp ou Chat pour site web par ex). Le tout depuis une seule interface.

Comme notre solution est utilisée H24 avec un grand niveau de disponibilité nécessaire, il était important pour nous de ne rien casser au moment du changement.

Pourtant, c’est également important pour nous d’améliorer notre solution chaque jour pour corriger des bugs et créer de nouvelles fonctionnalités.

C’est pourquoi nous avons créé deux branches: Une pour Vue.js 2.6, la version en production et une branche Vue.js 3, où nous avons migré tous le code.

Pour un process le plus smooth possible (🥤), nous déployons chaque jour une beta, accessible à nos utilisateurs les plus fidèles et aux employés de Crisp. Dans le même temps, nous rétroportons également ces changements sur la version 2.6.

L’idée c’est d’éviter les maux de crâne au moment de la fusion complète.

Finalement, nous avons choisi de ne pas capitaliser sur les nouvelles API Vue comme l’API composition. L’idée c’était de n’utiliser que le nécessaire pour éviter le risque de régressions.

Mettre à jour le constructeur Vue.js

Notre logiciel de service client s’appuie sur Vue Cli (webpack). Nous avons choisi de ne pas migrer sur Vite pour le moment. L’idée était d’éviter toute complexité et donc de générer de nouveaux bugs car notre système est plutôt complexe 😁.

Migrer Vue Cli afin de supporter Vue 3 est vraiment très simple.

  1. Editez votre package.json afin de mettre à jour Vue and ses dépendances
  2. Remplacez "vue": "^2.6.12" par "vue": "^3.1.0"
  3. Ajouter la compatibilité "@vue/compat": "^3.1.0" pour une migration smooth (🥤) de Vue 2 à 3
  4. Mettez à jour  "vue-template-compiler": "^2.6.12" à "@vue/compiler-sfc": "^3.1.0"

Maintenant, vous n’avez plus qu’à mettre à jour votre fichier vue.config.js et d’éditer la fonction chainWebpack. Cela va forcer toutes vos librairies à utiliser le package @vue/compat.

  // Vue 2 > Vue 3 compatibility mode
  config.resolve.alias.set("vue", "@vue/compat");

  config.module
    .rule("vue")
    .use("vue-loader")
    .loader("vue-loader")
    .tap(options => {
      // Vue 2 > Vue 3 compatibility mode
      return {
        ...options,
        compilerOptions: {
          compatConfig: {
            // default everything to Vue 2 behavior
            MODE: 2
          }
        }
      };
    });

 

Mettre à jour notre fichier main.js

Maintenant, il faut instancier Vue de la façon suivante

import { createApp, h, configureCompat } from "vue";

const app = createApp({
  render: () => h(App)
});

app.use(router);
app.use(store);
// Initiate other plugins here

configureCompat({
  // default everything to Vue 2 behavior
  MODE: 2,
});

app.mount("#app");

 

Mettre à jour Vue Router

Il nous faut utiliser la dernière version de Vue Router "vue-router": "4.0.11"

La dernière version n’a pas de gros changements. La différence principale réside dans le fait que vous allez devoir activer l’historique de la façon suivante:

import { createWebHistory, createRouter } from "vue-router";

var router = createRouter({
  history: createWebHistory(),
  routes: [
    // all your routes
  ]
});

Mettre à jour les filtres (⚠️)

Notre solution se base énormément sur l’utilisation de filtres (environ 200). La première chose était de trouver combien de filtres nous avions dans notre code. Comme les IDEs moderne (VSCode, SublimeText) supportent la recherche via Regex, nous avons créé notre propre regex pour trouver tous nos filtres Vue: {{ (.*?\|(.*?) }}

Ça a été le plus gros challenge de notre migration, et voici ce que nous avons mis en place pour gérer au mieux ce gros changement.

Vue 3 a totalement arrêté de supporter les filtres, nous avons ainsi cherché une façon belle et subtile de continuer à les utiliser.

Pour ça, nous sommes passer des filtres Vue à des assistants Singletons personnalisés.

Par exemple sur Vue 2, nous avions:

Vue.filter("uppercase", function(string) {
  return string.toUpperCase();
});

Alors que sur Vue 3 nous avons désormais:

uppercase(string) {
  return string.toUpperCase();
}

export { uppercase };

Par la suite, nous lançons l’instance de la façon qui suit en utilisant la class StringsFilter:

import  { uppercase } from "@/filters/strings";

app.config.globalProperties.$filters = {
  uppercase: uppercase
};

Grâce à cela, nous pouvons utiliser nos filtres Vue 2 de la façon suivante {{ $filters.uppercase(firstName) }}

Gérer les erreurs

Au cours de votre migration, vous allez voir apparaître des erreurs dans la console de votre navigateur. Le mode de compatibilité de Vue 3 vient avec de nombreux logs pour vous aider à migrer au mieux.

Plutôt que d’essayer de migrer votre application fonctionnalité par fonctionnalité, nous vous recommandons de migrer API par API.

Par exemple, si vous regardez les alertes de dépréciations « WATCH_ARRAY », nous vous conseillons de jeter un coup d’oeil au guide de migration fourni dans les logs de la console de votre navigateur préféré.

Une fois la migration réalisée, vous pouvez arrêter la compatibilité avec ce mode:

import { createApp, h, configureCompat } from "vue";

const app = createApp({
  render: () => h(App)
});

// Initiate all plugins here

configureCompat({
  // default everything to Vue 2 behavior
  MODE: 2,
  
  // opt-in to Vue 3 behavior for non-compiler features
  WATCH_ARRAY: false
});

app.mount("#app");

Mettre à jour les librairies

Vue 3 apporte le package @vue/compat, permettant de supporter Vue.js 2 et Vue.js 3. Cependant, le mode @vue/compat génère également une dégradation des performances.

Utiliser le mode de compatibilité ne devraient être fait que lorsque vous faites la transition. Une fois que le projet les librairies ont été converties, you pouvez arrêter l’utilisation de cette option.

Toutes les librairies Vue officielles ont été portée à Vue 3, tandis que les packages les plus populaires ont déjà des compatibilité avec Vue 3.

Environ 20% des librairies communautaires que nous utilisions n’avaient pas encore de compatibilité Vue 3, nous avons donc forké toutes les librairies qui n’avaient pas fait le nécessaire.

Par exemple, nous l’avons récemment fait pour vue-router-prefetch.

Porter une librairie sur Vue 3 est généralement très simple et ne prend pas plus d’une demi-journée. Pour certaines, cela n’a pris que quelques minutes. Cela dépend bien évidemment de la complexité de la librairie évoquée.

Au sujet des performances

Vue.js 3 apporte avec de nombreuses améliorations grâce au nouveau système de réactivité. La taille du heap Javascript a été réduit de 20 à 30% en fonction des cas et pour plus de 50% pour d’autres plus complexes.

La finalité? Un confort d’utilisation immédiatement quantifiable pour tous nos clients.

performance Vue.js

Conclusion

En écrivant cet article, nous venons de publier la nouvelle app Crisp en production. Migrer cette application nous a pris deux semaines pour une équipe de deux personnes et 250 000 lignes de code. À titre de comparaison, migrer de Angular 1 à Vue 2 nous a pris 9 mois.

Grâce à Vue, nous pouvons désormais offrir une expérience exceptionnelle à tous nos clients par rapport. Pour remercier la communauté Vue.js, nous venons d’annoncer notre contribution mensuelle au projet à hauteur de 500€ / mois.

Si toutes les entreprises utilisant des projets open-source avaient la possibilité de financer les projets qu’ils utilisent, Internet serait aujourd’hui bien plus forte et indépendante qu’elle ne l’est actuellement.

Initialement, cet article a été rédigé en anglais sur notre blog.

 

Le rédaction de WeLoveDevs vous recommande également l’article d’Antoine & Crisp publié il y a quelques mois : Économiser 42Ko pour sauver 50Tb de bande passante ! 

Antoine Goret

View Comments

Recent Posts

MICI au travail : Le handicap invisible qui révèle des forces insoupçonnées

Les maladies inflammatoires chroniques de l’intestin ou "MICI" sont invisibles, mais leurs impacts sur la…

9 heures ago

Exploiter les NPUs pour de l’IA embarquée dans les applis webs

Depuis l'été, j'ai un Pixel qui intègre à la fois un TPU (Tensor Processing Unit)…

4 jours ago

Qcm saison hiver 2024 : toutes les infos.

On se retrouve dans un nouvel article avec toutes les infos sur cette nouvelle saison…

3 semaines ago

L’inclusion numérique est essentielle.

Pourquoi l’inclusion numérique est essentielle : le point avec Mathieu Froidure. Dans un monde de…

3 semaines ago

Communauté Tech et féminine : Interview avec Helvira de Motiv’her

Elles sont passées où les femmes dans la tech ? Entre le manque de représentation…

4 semaines ago