Vue mixins, a maneira explícita (por um exemplo de plugin de modificadores BEM)


Vue mixins são a maneira recomendada de compartilhar funcionalidades comuns entre componentes. Eles estão perfeitamente bem até você usar mais de um para eles. Isso ocorre porque eles estão implícitos no design e poluem o contexto do seu componente. Vamos tentar consertar isso, dando a eles o máximo de explicações possível.


Objetivo


Gostaríamos de ter um mixin global que dê a qualquer componente um suporte chamado types e produz uma matriz de classes CSS chamadas mods derivadas de baseClass .


Dada esta marcação:


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

Esperamos ter isso (assumindo que nossa baseClass seja sample-component ):


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

Abordagem ingênua


Ao ler apenas a documentação do Vue, seu primeiro pensamento pode ser usar apenas a mesclagem de propriedades incorporada e fornecer mods como uma propriedade computada em um componente desejado.


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

Essa abordagem sofre de muitos problemas:


  1. Código padrão em todos os componentes. (pelo menos em uma abordagem Vue)
  2. Dependência de um argumento baseClass .
  3. Nenhuma indicação clara de onde a propriedade mods veio.
  4. Conflitos de nomes são facilmente possíveis.

Vamos tentar corrigir todos esses problemas na próxima etapa.


Mixin com uma exportação explícita


O Vue possui um mecanismo de injeção de dependência, chamado Inject \ Provide . Pode potencialmente resolver nosso problema com o contexto poluidor.


Inicialmente, vamos mudar de um mixin simples para um plug-in, que aceita opções, que usaremos posteriormente para evitar conflitos de nome.


Em segundo lugar, também podemos reutilizar o nome de nosso componente como uma baseClass e não incluir isso como uma opção personalizada em cada componente.


E, por último, deixaremos uma opção para passar baseClass como argumento de função, caso a baseClass do nosso componente não corresponda ao seu nome.


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

Agora estamos prontos para registrar nosso plugin globalmente.


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

Também podemos personalizar como nossos objetos são chamados, fornecendo um objeto de opções.


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

E aqui está como nosso componente se parece após a refatoração da mixagem:


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

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

Vamos imaginar que nosso componente não tem name ou tem uma baseClass diferente do nome:


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

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

Ou se queremos estar prontos para uma refatoração ou remoção de plug-in:


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

Você também pode usar o Symbol como um nome de mods para eliminar completamente os conflitos de nomes, mas isso exigiria a inclusão desse símbolo em todos os componentes em que você gostaria de usar o bemMods .


Não especificamos implicitamente nosso nome de prop, mas essa é uma limitação principal do mixin, que tentamos superar com um nome de prop customizado em uma configuração de plug-in.


Espero que isso tenha sido útil para você e você encontrou uma maneira melhor de escrever mixins para o Vue.

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


All Articles