Vue mixins, la forma explícita (por un ejemplo de plugin de modificadores BEM)


Los mixins de Vue son la forma recomendada de compartir funcionalidades comunes entre componentes. Están perfectamente bien hasta que use más de uno para ellos. Esto se debe a que están implícitos por diseño y contaminan el contexto de su componente. Tratemos de arreglar esto dándoles tanta explicitación como podamos.


Gol


Nos gustaría tener un mixin global que proporcione a cualquier componente un accesorio llamado types y genere una matriz de clases CSS llamadas mods que se derivan de baseClass .


Dado este marcado:


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

Esperaríamos tener esto (suponiendo que nuestra baseClass sea sample-component ):


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

Enfoque ingenuo


Al leer solo la documentación de Vue, su primer pensamiento podría ser simplemente usar la combinación de propiedades incorporada y proporcionar mods como una propiedad calculada en un componente deseado.


 // 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') ] } 

Este enfoque tiene muchos problemas:


  1. Código repetitivo en cada componente. (al menos en un enfoque Vue)
  2. Dependencia de un argumento baseClass .
  3. No hay una indicación clara de dónde proviene la propiedad mods .
  4. Los conflictos de nombres son fácilmente posibles.

Intentaremos solucionar todos estos problemas en el siguiente paso.


Mixin con una exportación explícita


Vue tiene un mecanismo de inyección de dependencia, llamado Inject \ Provide . Potencialmente puede resolver nuestro problema con el contexto contaminante.


Al principio, cambiemos de un simple mixin a un complemento, que acepta opciones, que usaremos más adelante para evitar conflictos de nombres.


En segundo lugar, también podemos reutilizar el nombre de nuestro componente como baseClass y no incluirlo como una opción personalizada en cada componente.


Y, por último, dejaremos una opción para pasar baseClass como argumento de función en caso de que baseClass nuestro componente no coincida con su nombre.


 // 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 }; 

Ahora estamos listos para registrar nuestro complemento a nivel mundial.


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

También podemos personalizar cómo se llaman nuestros accesorios, proporcionando un objeto de opciones.


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

Y así es como se ve nuestro componente después de la refactorización mixin:


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

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

Imaginemos que nuestro componente no tiene name o tiene una baseClass diferente de su nombre:


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

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

O si queremos estar listos para una refactorización o eliminación de complementos:


 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', } } } 

También puede usar Symbol como un nombre de mods para eliminar completamente los conflictos de nombres, pero eso requeriría que incluya ese símbolo en cada uno de los componentes donde le gustaría usar bemMods .


No especificamos implícitamente nuestro nombre de utilería, pero esa es una limitación de mezcla principal, que intentamos superar con un nombre de utilería personalizado en una configuración de complemento.


Espero que esto te haya sido útil y hayas encontrado una mejor manera de escribir mixins para Vue.

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


All Articles