Table of Contents
Comprendre le modèle de prototype
Le modèle Prototype est un modèle de conception qui se concentre sur le concept de clonage d'objets existants plutôt que de construire de nouveaux objets à partir de zéro. Il est particulièrement utile lorsque le coût de création d'une nouvelle instance d'une classe est plus cher que de copier un objet existant, ou lorsque le système doit être indépendant de la façon dont ses objets sont créés.
Le motif repose sur un objet prototype qui sert de plan. Tout nouvel objet créé à partir de ce prototype hérite de ses propriétés et méthodes. Le nouvel objet peut alors être personnalisé sans affecter le prototype original. Cette approche réduit la redondance, encourage la cohérence et simplifie la maintenance – lorsque le prototype change, tous les clones basés sur lui sont automatiquement mis à jour (dans des références en direct de la chaîne prototype).
Dans le contexte du développement de l'interface utilisateur, le Prototype Pattern permet aux équipes de construire une bibliothèque d'éléments réutilisables qui partagent un comportement, un style et une logique communs. Au lieu de dupliquer le code pour des composants similaires, les développeurs clonent un composant de base et l'adaptent aux besoins spécifiques.
Le modèle de prototype dans Vue.js
Les composants Vue.js sont essentiellement des objets JavaScript qui définissent un modèle, des données, des méthodes, des propriétés calculées et des crochets de cycle de vie. En raison de cette nature orientée objet, Vue prend naturellement en charge l'héritage basé sur prototype.
- Extension de composant via : L'option Vue permet à un composant d'hériter d'un autre composant, fusionnant des options.
- Mixins: Un mixin est un bloc réutilisable d'options de composants qui peuvent être fusionnées en n'importe quel composant, en clonant efficacement le comportement sur plusieurs prototypes.
- Composables (Compposition API)[: Avec Vue 3, les fonctions composables permettent la réutilisation logique sans s'appuyer sur l'héritage d'objets, offrant une forme plus flexible de clonage prototype.
- Croing de composants dynamiques[: En utilisant ou avec ou en répartissant des opérateurs, les développeurs peuvent créer de nouvelles définitions de composants basées sur celles existantes à l'exécution.
Le modèle Prototype s'harmonise bien avec le système réactif de Vue. Lorsque vous clonez une définition de composant, le composant résultant réagit toujours aux changements de données et aux événements du cycle de vie comme prévu. Cela rend simple de construire une famille de composants d'interface utilisateur qui partagent un ensemble de fonctionnalités de base mais diffèrent dans l'apparence ou le comportement.
Prototype Pattern avec API Options
Dans l'API des options Vue 2 et Vue 3 vous pouvez utiliser l'option pour créer un composant enfant qui hérite d'un composant parent. Le composant enfant peut outrepasser toutes les options (données, méthodes, calcul, etc.) tout en conservant le reste. Ceci est l'implémentation la plus proche du modèle prototype classique.
// BaseButton.js
export default {
name: 'BaseButton',
props: {
label: { type: String, default: 'Click me' }
},
data() {
return {
isDisabled: false
}
},
methods: {
handleClick() {
this.$emit('click')
}
},
template: `<button :disabled="isDisabled" @click="handleClick" class="btn btn-primary">{{ label }}</button>`
}
// SecondaryButton.js
import BaseButton from './BaseButton'
export default {
extends: BaseButton,
data() {
return {
isDisabled: false
}
},
template: `<button :disabled="isDisabled" @click="handleClick" class="btn btn-secondary">{{ label }}</button>`
}
Dans cet exemple, est un clone de avec un modèle modifié et une classe CSS. Les accessoires, les données et les méthodes sont hérités, mais peuvent être dépassés.
Prototype Pattern avec l'API Composition
L'API Composition de Vue 3 encourage l'utilisation de fonctions Composables au lieu d'héritage. Cependant, vous pouvez encore implémenter le concept prototype en créant une fonction d'usine qui retourne un objet de définition de composant, puis le répartir dans un nouveau composant. Cette approche est plus explicite et évite la fusion d'options profondes.
// useButtonPrototype.js
import { ref } from 'vue'
export function useButtonPrototype() {
const isDisabled = ref(false)
function handleClick(emit) {
emit('click')
}
return { isDisabled, handleClick }
}
// BaseButton.vue
<script setup>
import { useButtonPrototype } from './useButtonPrototype'
const emit = defineEmits(['click'])
const { isDisabled, handleClick } = useButtonPrototype()
</template>
<template>
<button :disabled="isDisabled" @click="handleClick(emit)" class="btn btn-primary">
<slot />
</button>
</template>
// SecondaryButton.vue
<script setup>
import { useButtonPrototype } from './useButtonPrototype'
const emit = defineEmits(['click'])
const { isDisabled, handleClick } = useButtonPrototype()
// Add secondary logic here
</template>
<template>
<button :disabled="isDisabled" @click="handleClick(emit)" class="btn btn-secondary">
<slot />
</button>
</template>
Ici, la logique prototype est extraite dans un composé, et chaque composant clone cette logique. Ce modèle est souvent plus durable que les chaînes d'héritage profondes.
Création d'un composant de base réutilisable
La première étape dans l'application du modèle Prototype dans Vue est de concevoir un composant de base solide. Ce composant devrait encapsuler la structure et le comportement communs que toutes les variantes partageront. Pour un élément UI comme un bouton, qui comprend:
- Un enveloppeur ou avec des attributs accessibles.
- Prop. pour la personnalisation: , , , etc.
- Fentes pour la projection de contenu.
- Émetteurs d'événements pour les interactions.
- Style courant via des classes CSS ou un système de conception.
Un composant de base bien conçu agit comme prototype. Il devrait être suffisamment souple pour gérer la plupart des cas d'utilisation sans être trop générique. Par exemple, un composant peut inclure un fond, un bouton de fermeture, une transition et une fente pour le contenu, tout en permettant aux variantes d'ajouter des en-têtes, des pied de page ou des animations personnalisés.
<template>
<transition name="fade">
<div v-if="visible" class="dialog-overlay" @click.self="close">
<div class="dialog" :class="sizeClass">
<button class="dialog-close" @click="close">×</button>
<div class="dialog-body">
<slot />
</div>
</div>
</div>
</transition>
</template>
<script>
export default {
props: {
visible: Boolean,
size: { type: String, default: 'medium' }
},
computed: {
sizeClass() { return `dialog--${this.size}` }
},
methods: {
close() { this.$emit('update:visible', false) }
}
}
</script>
Cette boîte de dialogue de base peut ensuite être clonée pour créer des dialogues de confirmation, des dialogues de formulaire ou des modes informationnels en étendant sa fonctionnalité.
Clonage et extension des composants
Vue fournit plusieurs façons de cloner et d'étendre les composants. L'option est la plus simple pour les petites modifications. Cependant, soyez prudent avec un héritage profond: Vue fusionne les options dans un ordre spécifique (composante ses options > extends > mixins).
Utilisation pour les variantes simples
// ConfirmDialog.js
import BaseDialog from './BaseDialog.vue'
export default {
extends: BaseDialog,
props: {
confirmText: { type: String, default: 'OK' },
cancelText: { type: String, default: 'Cancel' }
},
template: `
<BaseDialog v-bind="$props" v-on="$listeners">
<template #default>
<slot />
</template>
<template #footer>
<button @click="close">{{ cancelText }}</button>
<button @click="$emit('confirm')">{{ confirmText }}</button>
</template>
</BaseDialog>
`,
components: { BaseDialog }
}
Notez que utilise à la fois comme prototype (par ) et comme composant enfant dans le modèle. Ce modèle vous permet d'ajouter un pied de page avec des boutons tout en réutilisant la logique de superposition, de transition et de fermeture de la boîte de dialogue de base.
Clonage programmatique avec (Vue 2) ou (Vue 3)
Pour les scénarios dynamiques, vous pouvez cloner une définition de composant à l'exécution.
import { defineComponent, h } from 'vue'
import BaseButton from './BaseButton.vue'
function createButtonVariant(customProps) {
return defineComponent({
extends: BaseButton,
setup(props, { slots }) {
// Override or extend behavior
return () => h(BaseButton, { ...customProps, ...props }, slots)
}
})
}
Cette technique est utile pour la construction d'un registre de composants qui génère des variantes basées sur des objets de configuration à partir d'un CMS ou d'une API.
Utilisation des Fentes pour la personnalisation
Les fentes sont une caractéristique centrale de Vue qui s'alignent parfaitement avec le modèle de prototype. En définissant les fentes dans le composant de base, vous permettez aux clones de remplacer ou d'augmenter des parties de l'interface utilisateur sans réécrire le composant entier.
Par exemple, un composant peut avoir des fentes pour , et . Un clone pourrait remplir l'en-tête d'un titre, la fente par défaut avec une image et une description, et le pied de page avec un bouton. Le prototype de base reste inchangé, mais le clone peut produire des cartes entièrement différentes.
Mélanges, extensions et composites : que utiliser ?
Le modèle Prototype peut être implémenté avec des mixins, des extensions ou des composants. Voici une comparaison rapide:
- Mixins: Utile quand vous voulez partager le comportement entre plusieurs composants non liés. Cependant, les mélanges peuvent conduire à des collisions et à une source de propriétés peu claire. Ils sont une forme de clonage prototype, mais sont souvent découragés en faveur des composants dans Vue 3.
- Extends: Meilleur pour créer une hiérarchie directe des composants parent-enfant. Il est intuitif lorsque vous avez un prototype de base clair et une variante qui hérite de toutes les options. L'inconvénient est qu'il crée un couplage serré.
- Composables: L'approche la plus flexible. Ils vous permettent d'extraire la logique en fonctions réutilisables pouvant être composées en n'importe quel composant. Composables ne créent pas de chaîne prototype; au lieu de cela, ils clonent le comportement par invocation de fonction.
Pour la plupart des bibliothèques d'éléments d'interface utilisateur, une combinaison fonctionne mieux : utilisez un composant de base pour la réutilisation logique, puis clonez ce composant de base pour les variantes en l'important et en l'enveloppant.
Exemple du monde réel : Construire un système de notification réutilisable
Imaginez que vous devez afficher les notifications dans votre application : succès, erreur, avertissement et info. Chaque variante partage la même disposition (icône, message, bouton de fermeture) mais diffère en couleur et en icône. En utilisant le modèle de prototype, vous pouvez créer un composant et le cloner pour chaque type.
- Définir avec des accessoires pour , et des fentes pour icône. Inclure la logique pour le rejet automatique et l'animation.
- Créer un ensemble de notifications qui gère une file d'attente (similaire à un pool prototype).
- Créer des composants de notification individuels comme qui s'étend ou compose avec un schéma de couleur verte et une icône de coche.
- Enregistrer ces composants à l'échelle mondiale ou utiliser un composant dynamique basé sur le type.
L'avantage : toutes les notifications partagent les mêmes attributs de logique de rejet, de transition et d'accessibilité. Modifier le composant de base (p. ex. ajouter swipe-to-dismiss) met automatiquement à jour chaque variante.
Avantages et limites du modèle de prototype
Avantages:
- Ressource de code: Réduit la duplication des éléments d'interface utilisateur.
- Consistance[: Tous les clones partagent le même comportement de base et le même style, assurant une expérience utilisateur uniforme.
- Facile d'entretien: Un seul changement au prototype se propage à tous les descendants (si on utilise l'héritage) ou peut être reproduit par des composés.
- Développement rapide[: De nouvelles variantes peuvent être créées en clonant un composant existant et en ne apportant que les changements nécessaires.
- Testabilité: Les essais de prototype de base couvrent des fonctionnalités communes; seuls des essais spécifiques à des variantes doivent être écrits.
Limitations:[
- Surface du clonage[: Dans JavaScript, le clonage d'objets peut être superficiel ou profond.
- Raccordement serré en héritage: L'utilisation de peut rendre les variantes fragiles si le composant de base change de structure interne. Le composant de base doit être conçu avec soin pour supporter les points d'extension.
- Complexité dans les hiérarchies profondes: Plusieurs niveaux d'héritage peuvent devenir difficiles à raisonner. Préférez la composition sur l'héritage lorsque c'est possible.
- Potentiel pour la duplication dans les templates: Si les clones doivent surcharger de grandes parties du template, vous pourriez finir par copier le template entier plutôt que de l'hériter. L'utilisation de slots atténue cela.
Comparaison avec le modèle d'usine
Le modèle Factory crée également des objets, mais il sépare la création d'objets du code client. Par contre, le modèle Prototype repose sur le clonage d'une instance existante. Dans Vue, une fonction d'usine peut renvoyer une nouvelle définition de composant basée sur des paramètres, tandis qu'une approche basée sur prototype commencerait par un composant de base et modifierait un clone. Les deux sont utiles; les usines sont souvent meilleures lorsque vous devez assembler des composants de plusieurs sources, tandis que les prototypes excellent lorsque vous avez une base solide qui a juste besoin de légères variations.
Meilleures pratiques pour utiliser le modèle de prototype dans la vue
- Des prototypes conçus avec extension en tête[: Utilisez les accessoires, les fentes et les événements de façon libérale.
- Préférence des composants sur les mélanges dans Vue 3. Ils fournissent des dépendances explicites et évitent de nommer des collisions.
- Profondeur de l'héritage : Pas plus de deux ou trois niveaux. Les chaînes profondes deviennent difficiles à déboguer.
- Documenter l'interface du prototype: Préciser clairement quels accessoires, quels emplacements et quelles méthodes sont destinés à être dépassés. Cela aide d'autres développeurs à créer des clones cohérents.
- Utiliser TypeScript[: Définir des interfaces pour les options et les accessoires des composants rend le clonage plus sûr et plus prévisible.
- Éviter les instances de composants de clonage à l'exécution si vous pouvez utiliser des définitions de composants statiques. Le clonage dynamique peut être inefficace et difficile à maintenir.
- Testez le prototype indépendamment. Ensuite, testez chaque variante uniquement pour ses personnalisations.
Avancé : Clonage de composants programmatiques avec
Dans Vue 3, vous pouvez créer une usine qui retourne une nouvelle définition de composant en clonant une base et en appliquant des redéfinitions. Ceci est utile pour créer des bibliothèques d'interface utilisateur hautement configurables qui sont utilisées dans différents projets avec différents systèmes de conception.
import { defineComponent, h } from 'vue'
function createClonedComponent(base, overrides = {}) {
return defineComponent({
name: overrides.name || `Cloned${base.name}`,
extends: base,
setup(props, { slots, emit }) {
// Apply overrides logic
const merged = { ...base.setup?.(props, { slots, emit }), ...overrides.setup?.(props, { slots, emit }) }
return merged
}
})
}
const SuccessNotification = createClonedComponent(BaseNotification, {
name: 'SuccessNotification',
setup() {
// Add success-specific logic
const icon = 'check-circle'
return { icon }
}
})
Cette approche vous permet de générer une trousse d'interface utilisateur complète à partir d'un ensemble de prototypes et de fichiers de configuration.
Conclusion
Le modèle Prototype est un outil puissant dans l'arsenal du développeur Vue.js, permettant la création d'éléments d'interface utilisateur réutilisables qui sont cohérents, durables et faciles à étendre. En comprenant comment cloner des composants par , des mixins ou des composants composites, et en concevant des composants de base avec des fentes et des accessoires, vous pouvez construire une bibliothèque de composants flexibles qui s'adapte aux exigences changeantes avec un minimum d'effort.
Pour plus de détails, explorez Vue.js Documentation de l'API de composition et Patterns.dev: Prototype Pattern. Consultez aussi les discussions sur Vue.extend vs mixins sur Stack Overflow pour obtenir des informations communautaires.