Vue mixins, la manière explicite (par un exemple de plugin BEM modifiers)


Les mixins Vue sont le moyen recommandé de partager des fonctionnalités communes entre les composants. Ils sont parfaitement bien jusqu'à ce que vous en utilisiez plus d'un. En effet, ils sont implicites par conception et polluent le contexte de votre composant. Essayons de résoudre ce problème en leur donnant autant d'explication que possible.


Objectif


Nous aimerions avoir un mixin global qui donne à n'importe quel composant un accessoire appelé types et génère un tableau de classes CSS appelées mods qui dérivent de baseClass .


Compte tenu de ce balisage:


 <SampleComponent :types="['active', 'block']"></SampleComponent> 

Nous nous attendrions à avoir ceci (en supposant que notre baseClass est sample-component ):


 <div class="sample-component sample-component--active sample-component--block"></div> 

Approche naïve


En lisant uniquement la documentation Vue, votre première pensée pourrait être d'utiliser simplement la fusion de propriétés intégrée et de fournir des mods comme propriété calculée dans un composant souhaité.


 // bemMods.js export default (baseClass) => ({ props: { types: { type: Array, default: () => [] } }, computed: { mods() { return this.types.map(type => `${baseClass}--${type}`); } } }) 

 // SampleComponent.vue <template> <div class="sample-component" :class="mods"><slot /></div> </template> 

 import bemMods from 'bemMods.js'; export default { name: 'SampleComponent', mixins: [ bemMods('sample-component') ] } 

Cette approche souffre de nombreux problèmes:


  1. Code de chaudière dans chaque composant. (au moins dans une approche Vue)
  2. Dépendance d'un argument baseClass .
  3. Aucune indication claire d'où vient la propriété des mods .
  4. Les conflits de noms sont facilement possibles.

Nous essaierons de résoudre tous ces problèmes dans une prochaine étape.


Mixin avec une exportation explicite


Vue dispose d'un mécanisme d'injection de dépendances, appelé Inject \ Provider . Cela peut potentiellement résoudre notre problème avec un contexte polluant.


Dans un premier temps, passons d'un simple mixin à un plugin, qui accepte des options, que nous utiliserons plus tard pour éviter les conflits de noms.


Deuxièmement, nous pouvons également réutiliser le nom de notre composant comme baseClass et ne pas l'inclure comme option personnalisée dans chaque composant.


Enfin, nous laisserons une option pour passer baseClass comme argument de fonction au cas où la baseClass notre composant ne correspondrait pas à son nom.


 // bemMods.js // Converts ComponentName to component-name const transformName = string => string.replace(/\s+/g, '-').toLowerCase(); const install = (Vue, { propName = 'types', modsName = 'mods' } = {}) => { Vue.mixin({ props: { // Prop name is now dynamic and allows to avoid conflits [propName]: { type: Array, default: () => [], } }, // Dependency injection forces us to explicitly require that function provide: { [modsName](baseClass) { baseClass = baseClass || transformName(this.$options.name); return (this[propName] || []).map(type => `${baseClass}--${type}`); } } }); }; export default { install }; 

Nous sommes maintenant prêts à enregistrer notre plugin dans le monde entier.


 import Vue from 'vue'; import bemMods from 'bemMods.js'; Vue.use(bemMods); 

Nous pouvons également personnaliser la façon dont nos accessoires sont appelés, en fournissant un objet options.


 import Vue from 'vue'; import bemMods from 'bemMods.js'; Vue.use(bemMods, { propName: 'modifiers', modsName: 'classes' }); 

Et voici à quoi ressemble notre composant après refactoring mixin:


 <template> <div class="sample-component" :class="mods"><slot /></div> </template> 

 export default { name: 'SampleComponent', // Explicit property inject: ['mods'] } 

Imaginons que notre composant n'a pas de name ou qu'il ait une baseClass différente de son nom:


 <template> <div class="special-component" :class="mods('snowflake')"><slot /></div> </template> 

 export default { name: 'SpecialComponent', inject: ['mods'] } 

Ou si nous voulons être prêts pour une refactorisation ou une suppression de plugin:


 export default { name: 'SomeComponent', inject: { // If mixin export property changes name it's now possible to replace it in every single component instance withouth any additional rework 'mods': { // In this case 'mods' becomes 'classes' from: 'classes', } } } 

Vous pouvez également utiliser Symbol comme nom de mods pour éliminer complètement les conflits de noms, mais cela vous obligerait à inclure ce symbole dans chaque composant où vous souhaitez utiliser bemMods .


Nous n'avons pas spécifié implicitement notre nom de prop, mais c'est une limitation de mixage de base, que nous avons essayé de surmonter avec un nom de prop personnalisé dans une configuration de plugin.


J'espère que cela vous a été utile et que vous avez trouvé une meilleure façon d'écrire des mixins pour Vue.

Source: https://habr.com/ru/post/fr441540/


All Articles