Passer au contenu

Règles de priorité A : Essentielles

Ces règles aident à prévenir les erreurs, donc apprenez les et tenez-y vous coûte que coûte. Il peut y avoir des exceptions, mais elles sont rares et devraient être faites par ceux ayant une expertise à la fois dans JavaScript et dans Vue.

Utilisez des noms de composants avec plusieurs mots

Les noms de composants créés par l'utilisateur devraient toujours être composés de plusieurs mots, à l'exception du composant racine App. Cela évite les conflits avec les éléments HTML existants et à venir, puisqu'ils sont tous composés d'un seul mot.

À éviter

template
<!-- dans les templates pré-compilés -->
<Item />

<!-- dans les templates à l'intérieur du DOM -->
<item></item>

OK

template
<!-- dans les templates pré-compilés -->
<TodoItem />

<!-- dans les templates à l'intérieur du DOM -->
<todo-item></todo-item>

Utilisez des définitions de prop détaillées

Dans le code source, les définitions de prop devraient toujours être le plus détaillées possible, en spécifiant au moins le ou les types.

Explications détaillées

Les définitions de prop détaillées présentent deux avantages :

  • Elles documentent l'API du composant, de manière à ce qu'il soit facile de voir comment le composant doit être utilisé.
  • Pendant le développement, Vue vous avertira si jamais un composant se voit fournir des props au mauvais format, ce qui vous aidera à régler les potentielles sources d'erreurs.

À éviter

js
// OK seulement lorsque vous prototyper
props: ['status']

OK

js
props: {
  status: String
}
js
// Encore mieux !
props: {
  status: {
    type: String,
    required: true,

    validator: value => {
      return [
        'syncing',
        'synced',
        'version-conflict',
        'error'
      ].includes(value)
    }
  }
}

À éviter

js
// OK seulement lorsque vous prototyper
const props = defineProps(['status'])

OK

js
const props = defineProps({
  status: String
})
js
// Encore mieux !

const props = defineProps({
  status: {
    type: String,
    required: true,

    validator: (value) => {
      return ['syncing', 'synced', 'version-conflict', 'error'].includes(
        value
      )
    }
  }
})

Utilisez v-for avec une clé

key avec v-for est toujours requis sur les composants, afin de maintenir l'état interne du composant à travers le sous-arbre. Cependant même pour les éléments, une bonne pratique est de maintenir un comportement prédictible, telle que la persistance de l'objet dans les animations.

Explications détaillées

Imaginons que vous ayez une liste de todos :

js
data() {
  return {
    todos: [
      {
        id: 1,
        text: 'Learn to use v-for'
      },
      {
        id: 2,
        text: 'Learn to use key'
      }
    ]
  }
}
js
const todos = ref([
  {
    id: 1,
    text: 'Learn to use v-for'
  },
  {
    id: 2,
    text: 'Learn to use key'
  }
])

Puis vous les triez par ordre alphabétique. Lors de la mise à jour du DOM, Vue va optimiser le rendu de manière à réaliser les mutations du DOM les moins coûteuses. Cela peut signifier supprimer la première todo, puis la remettre à la fin de la liste.

Le problème est que, dans certains cas il peut être important de ne pas supprimer d'éléments qui vont rester dans le DOM. Par exemple, vous pouvez vouloir utiliser <transition-group> pour animer le tri de la liste, ou maintenir le focus si l'élément rendu est un <input>. Dans ces cas, ajouter une clé unique pour chaque item (par exemple :key="todo.id") précisera de manière plus prédictible à Vue comment se comporter.

De notre expérience, il est préférable de toujours ajouter une clé unique, de sorte que vous et votre équipe n'ayez jamais à vous soucier de ces rares cas. Dans les scenarios rares et critiques, d'un point de vue des performances, où la persistance de l'objet n'est pas nécessaire, vous pouvez faire une exception.

À éviter

template
<ul>
  <li v-for="todo in todos">
    {{ todo.text }}
  </li>
</ul>

OK

template
<ul>
  <li
    v-for="todo in todos"
    :key="todo.id"
  >
    {{ todo.text }}
  </li>
</ul>

Évitez les v-if avec les v-for

N'utilisez jamais v-if et v-for sur le même élément.

Il y a deux situations courantes où cela peut être tentant :

  • Pour filtrer des items dans une liste (par exemple v-for="user in users" v-if="user.isActive"). Dans ces cas, remplacez users par une nouvelle propriété calculée qui vous retourne votre liste filtrée (par exemple activeUsers).

  • Pour éviter le rendu d'une liste si elle doit être cachée (par exemple v-for="user in users" v-if="shouldShowUsers"). Dans ces cas, déplacez le v-if sur un élément conteneur (par exemple ul, ol).

Explications détaillées

Lorsque Vue traite les directives, v-if a la priorité sur v-for, donc ce template :

template
<ul>
  <li
    v-for="user in users"
    v-if="user.isActive"
    :key="user.id"
  >
    {{ user.name }}
  </li>
</ul>

Va produire une erreur, car la directive v-if sera d'abord évaluée et la variable d'itération user n'existe pas à ce moment précis.

Cela peut être résolu en itérant sur une propriété calculée à la place, de cette façon :

js
computed: {
  activeUsers() {
    return this.users.filter(user => user.isActive)
  }
}
js
const activeUsers = computed(() => {
  return users.filter((user) => user.isActive)
})
template
<ul>
  <li
    v-for="user in activeUsers"
    :key="user.id"
  >
    {{ user.name }}
  </li>
</ul>

Ou encore, nous pouvons utiliser une balise <template> avec v-for pour envelopper l'élément <li> :

template
<ul>
  <template v-for="user in users" :key="user.id">
    <li v-if="user.isActive">
      {{ user.name }}
    </li>
  </template>
</ul>

À éviter

template
<ul>
  <li
    v-for="user in users"
    v-if="user.isActive"
    :key="user.id"
  >
    {{ user.name }}
  </li>
</ul>

OK

template
<ul>
  <li
    v-for="user in activeUsers"
    :key="user.id"
  >
    {{ user.name }}
  </li>
</ul>
template
<ul>
  <template v-for="user in users" :key="user.id">
    <li v-if="user.isActive">
      {{ user.name }}
    </li>
  </template>
</ul>

Utilisez du style avec une portée limitée au composant

Pour les applications, les styles du composant App et des composants de mise en page peuvent être globaux, mais tous les autres styles des composants devraient avoir une portée limitée.

Cela n'est pertinent que pour les composants monofichiers. Cela ne nécessite pas que l'attribut scoped soit utilisé. La limitation de la portée peut se faire via des modules CSS, une stratégie basée sur les classes telle que BEM, ou tout autre librairie/convention.

Toutefois, pour les librairies de composants, il est préférable d'utiliser une stratégie basée sur les classes au lieu d'utiliser l'attribut scoped.

Cela permet de remplacer les styles internes plus facilement, avec des noms de classe compréhensibles par des humains et qui ne sont pas trop spécifiques, mais ont tout de même qu'une faible probabilité d'être à l'origine d'un conflit.

Explications détaillées

Si vous développez un projet conséquent, travaillez avec d'autres développeurs, or incluez parfois du HTML/CSS tiers (par exemple à partir d'Auth0), une gestion des portées cohérente assurera que vos styles ne s'appliquent qu'aux composants pour lesquels ils sont destinés.

Au-delà de l'attribut scoped, utiliser des noms de classe uniques permet de s'assurer que du CSS tiers ne s'applique pas à votre HTML. Par exemple, de nombreux projets utilise les noms de classe button, btn, ou icon, donc même si vous n'utilisez pas de stratégie comme BEM, ajouter un préfixe spécifique à votre application ou votre composant (par exemple ButtonClose-icon) peut fournir une certaine protection.

À éviter

template
<template>
  <button class="btn btn-close">×</button>
</template>

<style>
.btn-close {
  background-color: red;
}
</style>

OK

template
<template>
  <button class="button button-close">×</button>
</template>

<!-- Using the `scoped` attribute -->
<style scoped>
.button {
  border: none;
  border-radius: 2px;
}

.button-close {
  background-color: red;
}
</style>
template
<template>
  <button :class="[$style.button, $style.buttonClose]">×</button>
</template>

<!-- En utilisant des modules CSS -->
<style module>
.button {
  border: none;
  border-radius: 2px;
}

.buttonClose {
  background-color: red;
}
</style>
template
<template>
  <button class="c-Button c-Button--close">×</button>
</template>

<!-- En utilisant la convention BEM -->
<style>
.c-Button {
  border: none;
  border-radius: 2px;
}

.c-Button--close {
  background-color: red;
}
</style>
Règles de priorité A : Essentiellesa chargé