在扩展Vue.js组件方面我找不到很好的资源。在我从事的每个项目中,无论使用什么UI组件库,都存在Application Base组件,这些组件扩展了UI库组件以实施公司/应用程序的默认值和标准。
我正在尝试扩展Vue-Multiselect:https://vue-multiselect.js.org/,它具有大约30个道具和12个插槽。我要扩展的组件无关紧要-我只提到它是因为理想情况下我不想在实现中重复30个prop和12个插槽。
我只想对组件的行为进行两项更改:
使disabled
变得更聪明
Vue-Multiselect组件具有一个标准disabled
道具,该道具可以按预期工作:
<Multiselect :disabled="isDisabled" ...>
在我们的应用程序中,我们在Vuex中具有全局状态,该状态确定该应用程序是否为只读。我要避免的是要求开发人员将此状态传递给每个表单字段:
<Multiselect :disabled="readOnly || isDisabled" ...>
<OtherComponent :disabled="readOnly || someOtherCondition" ...>
...
因此,基本组件的用户只需要担心其本地UI状态会影响禁用状态:
<BaseCombo :disabled="!emailValid" ...>
这将处理90%的表单字段(当应用程序为只读状态时被锁定)的情况,在我们要忽略全局只读状态的情况下,我可以使用其他支持。
<BaseCombo :disabled="!emailValid" :ignoreReadOnly="true" ...>
提供默认值
第二,我只想覆盖一些默认的prop值。这篇文章解决了提供默认值的问题:
https://stackoverflow.com/a/52592047/695318
这非常有效,直到我尝试修改前面提到的禁用道具的行为为止。
我试图解决这个问题的方法是包装或扩展组件。如果可能的话,我真的想避免重新声明所有道具。
<template>
<Multiselect
:disabled="myCustomDisabled"
:value="value"
@input="$emit('input', $event)"
:options="options"
:label="label"
:track-by="trackBy"
:placeholder="placeholder"
... repeat for all 30 options
<script>
import Multiselect from 'vue-multiselect'
export default {
name: "BaseCombo",
extends: Multiselect, // extend or simply wrap?
computed: {
myCustomDisabled() {
this.props.disabled || ... use disabled from Vuex state
}
},
props: {
disabled: Boolean,
placeholder: {
type: String,
default: 'My Default Value',
},
... repeat for all props
我遇到的问题是我不知道如何处理插槽。该BaseCombo的用户仍应能够使用VueMultiselect组件中的所有12个插槽。
是否有更好的扩展组件解决方案?
答案 0 :(得分:1)
组件的组成更加灵活,但是对于任何想使用更简单的extends
选项(请参见Extending VueJS Components)的人来说,这就是一个例子。
注释
disabled
scoped
的形式传递VueMultiselect样式表(默认情况下不继承)<script>
import VueMultiselect from 'vue-multiselect'
export default {
name: 'MultiselectExtended',
extends: VueMultiselect,
props: {
disabled: {
type: Boolean,
default: function() {
return this.$store.getters.isReadOnly
}
},
},
}
</script>
<style src="vue-multiselect/dist/vue-multiselect.min.css" scoped></style>
答案 1 :(得分:0)
您可以使用this.$props
访问props属性中定义的props。同样,您可以使用this.$attrs
访问属性(尚未定义为道具的事物)。最后,您可以将道具与v-bind="someVariable"
绑定。
如果结合使用此功能,则可以执行以下操作:
<!-- App.vue -->
<template>
<component-a msg="Hello world" :fancy="{ test: 1 }" />
</template>
<!-- ComponentA.vue -->
<template>
<component-b v-bind="$attrs" />
</template>
<script>
export default {
name: 'componentA'
}
</script>
<!-- ComponentB.vue -->
<template>
<div>
{{ msg }}
{{ fancy }}
</div>
</template>
<script>
export default {
props: {
msg: String,
fancy: Object
},
mounted () {
console.log(this.$props);
}
}
</script>
在此示例中,组件B将是您尝试扩展的组件。
答案 2 :(得分:0)
这是一个基于Sumurai8的回答和motia的评论的完整示例。
<template>
<Multiselect v-bind="childProps" v-on="$listeners">
<slot v-for="(_, name) in $slots" :name="name" :slot="name" />
<template v-for="(_, name) in $scopedSlots" :slot="name" slot-scope="slotData">
<slot :name="name" v-bind="slotData" />
</template>
</Multiselect>
</template>
<script>
import Multiselect from 'vue-multiselect'
export default {
name: "BaseCombo",
props: {
placeholder: {
type: String,
default: 'This is my default',
},
disabled: {
type: Boolean,
default: false,
},
},
components: {
Multiselect,
},
computed: {
childProps() {
return { ...this.$props, ...this.$attrs, disabled: this.isDisabled };
},
appReadOnly() {
return this.$store.state.appReadOnly;
},
isDisabled() {
return this.disabled || this.appReadOnly;
}
},
}
</script>