Retour au cours

frontend / vuejs

Composition API vs Options API

Leçon 41 exercice

Explication

Ce que vous allez apprendre

  • Écrire le même composant en Options API et en Composition API, pour bien voir la différence
  • Comprendre pourquoi la Composition API organise le code par fonctionnalité plutôt que par type
  • Extraire de la logique réactive dans un composable réutilisable entre plusieurs composants
  • Identifier dans quel contexte chaque style d'API reste pertinent
  • Reconnaître que les deux API compilent vers le même moteur interne et peuvent cohabiter

Dans quel contexte ?

Une équipe reprend un projet Vue legacy écrit entièrement en Options API et souhaite y ajouter une nouvelle fonctionnalité de recherche avec debounce, réutilisable sur trois pages différentes. Avec l'Options API, cette logique devrait être dupliquée ou extraite via un mixin (une solution ancienne connue pour ses collisions de noms) ; avec la Composition API, un simple composable suffit. C'est précisément ce type de besoin qui a motivé la création de la Composition API.

D'abord, deux façons d'organiser le même composant

L'Options API structure un composant par TYPE d'option : toutes les données dans data(), toutes les méthodes dans methods, tous les calculs dérivés dans computed. Pour un petit composant, cette organisation reste très lisible et this donne un accès naturel à tout l'état.

La Composition API, à l'inverse, organise le code par FONCTIONNALITÉ : tout ce qui concerne le compteur (sa valeur, son double calculé, sa fonction d'incrémentation) est regroupé ensemble, peu importe qu'il s'agisse techniquement d'un état, d'un calcul ou d'une méthode.

Prérequis

Il faut avoir compris ref()/reactive() (leçon précédente) : la Composition API s'appuie directement sur ces primitives de réactivité.

Ensuite, où cette différence devient vraiment déterminante

Pour un petit composant isolé, les deux approches se valent largement. La différence explose dès qu'il faut RÉUTILISER de la logique entre plusieurs composants : l'Options API propose les "mixins", une solution limitée où deux mixins utilisant le même nom de propriété entrent en collision silencieusement.

CritèreOptions APIComposition API
OrganisationPar type (data/methods/computed)Par fonctionnalité
Réutilisation de logiqueMixins (collisions possibles)Composables (fonctions, pas de collision)
thisOmniprésentAbsent, juste des variables
Support TypeScriptCorrectExcellent (inférence naturelle)

Il reste à voir concrètement ce qu'un composable apporte

Une fonction comme useCompteur(pasInitial) encapsule un état réactif complet (valeur, pas, double calculé, fonction d'incrémentation) et peut être appelée depuis N'IMPORTE QUEL composant, chaque appel créant sa propre instance indépendante de cet état. Ce pattern sera approfondi dans une leçon dédiée plus loin dans ce cours.

Piège fréquent

Mélanger de façon désordonnée les deux styles dans un même composant (par exemple des propriétés data() ET un <script setup> séparé) crée une confusion inutile. Les deux API peuvent techniquement cohabiter dans un même PROJET, mais un même COMPOSANT devrait rester cohérent avec un seul style.

Maintenant, quel choix faire pour un nouveau projet

Bonne pratique

Pour tout projet neuf de taille moyenne à grande, la Composition API avec <script setup> est aujourd'hui la recommandation officielle de l'équipe Vue : meilleure réutilisation de logique, meilleur support TypeScript, et une syntaxe <script setup> qui élimine le boilerplate du return {} explicite. L'Options API reste pertinente pour de très petits composants isolés ou du code legacy déjà écrit ainsi.

Maintenant que le style Composition API est acquis, la prochaine leçon aborde comment les composants communiquent entre eux : la transmission de données du parent vers l'enfant via les props, et la remontée d'événements vers le parent via les emits.

Commandes & code

Composition API vs Options API

Deux façons d'écrire un composant Vue 3 ; la Composition API est aujourd'hui recommandée pour les projets non triviaux.

vue
<!-- Options API : organisation par TYPE d'option (data, methods, computed...) -->
<script>
export default {
  name: 'CompteurOptions',
  props: {
    pas: { type: Number, default: 1 },
  },
  data() {
    return {
      compteur: 0,
    }
  },
  computed: {
    double() {
      return this.compteur * 2
    },
  },
  watch: {
    compteur(nouvelleValeur, ancienneValeur) {
      console.log(`compteur: ${ancienneValeur} -> ${nouvelleValeur}`)
    },
  },
  methods: {
    incrementer() {
      this.compteur += this.pas   // `this` donne accès à tout l'état du composant
    },
  },
  mounted() {
    console.log('composant monté')
  },
}
</script>

<template>
  <button @click="incrementer">{{ compteur }} (double: {{ double }})</button>
</template>
vue
<!-- Composition API : organisation par FONCTIONNALITÉ (tout le code d'une feature ensemble) -->
<script setup>
import { ref, computed, watch, onMounted } from 'vue'

const props = defineProps({
  pas: { type: Number, default: 1 },
})

const compteur = ref(0)
const double = computed(() => compteur.value * 2)

watch(compteur, (nouvelleValeur, ancienneValeur) => {
  console.log(`compteur: ${ancienneValeur} -> ${nouvelleValeur}`)
})

function incrementer() {
  compteur.value += props.pas
}

onMounted(() => {
  console.log('composant monté')
})
</script>

<template>
  <button @click="incrementer">{{ compteur }} (double: {{ double }})</button>
</template>
js
// Le vrai avantage de la Composition API apparaît quand on EXTRAIT la logique
// dans un composable réutilisable entre plusieurs composants (impossible proprement en Options API).
import { ref, computed } from 'vue'

export function useCompteur(pasInitial = 1) {
  const compteur = ref(0)
  const pas = ref(pasInitial)
  const double = computed(() => compteur.value * 2)

  function incrementer() {
    compteur.value += pas.value
  }

  return { compteur, pas, double, incrementer }
}

// Dans N'IMPORTE QUEL composant :
// const { compteur, double, incrementer } = useCompteur(5)
CritèreOptions APIComposition API
Organisationpar type (data/methods/computed)par fonctionnalité
Réutilisation logiquemixins (limité, collisions de noms)composables (propre, typé)
thisomniprésentabsent (juste des variables)
Support TypeScriptcorrectexcellent (inférence naturelle)
Idéal pourpetits composants, migration legacyapplications de taille moyenne à grande

Résumé

  • Les deux API compilent vers le même moteur interne ; on peut les mélanger dans un même projet.
  • La Composition API brille dès qu'il faut partager de la logique entre composants (composables).
  • <script setup> est la syntaxe compacte recommandée pour écrire de la Composition API.
  • Pour un projet neuf de taille moyenne/grande, préférer systématiquement la Composition API.

Exercices pratiques

1 disponible
1

Mission : extraire une logique dupliquée trois fois

Objectif : Identifier pourquoi une logique de recherche avec debounce ne peut pas être proprement partagée en Options API, puis l'extraire en composable Composition API.

Contexte

Trois composants Options API (PageProduits, PageClients, PageCommandes) contiennent chacun un data() avec termeRecherche et resultats, une method rechercher() quasi identique avec un setTimeout de 300ms pour le debounce, et un watch sur termeRecherche. Le code est dupliqué trois fois à l'identique. On te demande de proposer une solution qui élimine cette duplication sans casser les trois pages.

Résoudre l’exercice →