Vue mixins,显式方式(通过BEM修饰符插件示例)


建议使用Vue mixin在组件之间共享通用功能。 除非您为它们使用多个,否则它们会很好。 这是因为它们在设计上是隐式的,并污染了组件的上下文。 让我们尝试通过为他们提供尽可能多的明确性来解决此问题。


目标


我们希望有一个全局的mixin,它为任何组件提供一个称为types baseClass ,并输出从mods继承自baseClass的CSS类数组。


鉴于此标记:


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

我们期望有这个(假设我们的baseClasssample-component ):


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

天真的方法


通过仅阅读Vue文档,您的第一个想法可能是只使用内置属性合并并在所需组件中将mods作为计算属性提供。


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

这种方法存在许多问题:


  1. 每个组件中的样板代码。 (至少在Vue方法中)
  2. baseClass参数的依赖。
  3. 没有明确指示mods属性来自何处。
  4. 名称冲突很容易发生。

我们将在下一步中尝试解决所有这些问题。


带有明确导出的Mixin


Vue有一个依赖注入机制,称为Inject \ Provide 。 它可以潜在地解决污染环境下的问题。


首先,让我们从一个简单的mixin切换到一个接受选项的插件,稍后我们将使用它来避免名称冲突。


其次,我们还可以将组件的名称作为baseClass而不必在每个组件中都将其作为自定义选项。


最后,如果组件的baseClass与名称不匹配,我们将保留将baseClass作为函数参数传递的选项。


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

现在,我们准备在全球范围内注册我们的插件。


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

我们还可以通过提供一个options对象来自定义道具的调用方式。


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

这是mixin重构后我们的组件的外观:


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

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

假设我们的组件没有name或者它的name具有不同的baseClass


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

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

或者,如果我们希望为重构或插件删除做好准备:


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

您还可以将Symbol用作mods名称,以完全消除名称冲突,但这将要求您在每个要使用bemMods组件中都包含该符号。


我们没有隐式指定道具名称,但这是一个核心的混合限制,我们试图通过在插件配置中使用自定义道具名称来克服。


希望这对您有所帮助,并且您找到了一种为Vue编写mixins的更好方法。

Source: https://habr.com/ru/post/zh-CN441540/


All Articles