可能做定制化开发的同学多少遇到过许多这种问题,就是自定义二次封装vue组件,前几天,试图封装一个vant的输入框,用来自定义一些聚焦和失去焦点时的不同样式,试了很久没有结果,主要是卡在了v-model无法传递这个点上了,苦思冥想加搜索,终于发现了解决之道。看正文:
第一部分:属性以及v-model
在进行Vue组件的二次封装时,如果想让v-model指令可用,需要在组件内部定义一个value属性和一个input事件,并将value属性绑定到子组件的某个属性上,同时在子组件的输入框上使用v-bind="$attrs"将父组件传递的所有属性绑定到子组件的输入框上,如下所示:
<!-- MyCustomInput.vue 子组件 -->
<template>
<input type="text" v-bind="$attrs" v-model="internalValue" @input="$emit('input', $event.target.value)">
</template>
<script>
export default {
name: 'MyCustomInput',
props: {
value: String // 父组件传递的 value 属性
},
data() {
return {
internalValue: this.value // 内部使用的 value 属性
};
},
};
</script>
在父组件中使用该自定义组件时,就可以像使用原生的元素一样使用v-model指令了,如下所示:
<!-- Parent.vue 父组件 -->
<template>
<div>
<my-custom-input v-model="message"></my-custom-input>
</div>
</template>
<script>
import MyCustomInput from './MyCustomInput.vue';
export default {
name: 'Parent',
components: {
MyCustomInput
},
data() {
return {
message: ''
};
}
};
</script>
在这个例子中,父组件中使用了v-model="message"指令来绑定数据到MyCustomInput组件中的value属性,该属性在子组件中使用了v-model="internalValue"指令进行绑定,并在input事件中通过$emit(‘input’, $event.target.value)事件将输入框的值传递给父组件。这样就实现了在自定义组件中使用v-model指令的功能。
第二部分:插槽和事件
讲到这里,就不得不说一下如何继承组件的插槽了,
插槽继承稍微麻烦一点,就是涉及多个插槽的时候,如何书写更加优雅,这里我不做不优雅的示范了,直接上优雅的干法:
下面是一个以 vant-field 标签为例的二次封装示例,演示如何继承原组件中的插槽,以二次封装vant-field标签为例:
<!-- MyCustomField.vue 子组件 -->
<template>
<van-field
v-bind="$attrs"
v-model="value"
v-on="$listeners"
>
<!-- 使用 $slots 继承原组件中的插槽 -->
<template v-for="(_, name) in $slots" :slot="name" :key="name">
<slot :name="name" />
</template>
</van-field>
</template>
<script>
import { Field } from 'vant';
export default {
name: 'MyCustomField',
components: {
VanField: Field
},
props: {
value: [String, Number],
label: String,
placeholder: String
}
};
</script>
在这个示例中,我们首先通过 import 语句引入了 vant-field 组件,并在 components 选项中注册了该组件。然后,我们使用 v-bind=“ a t t r s " 将父组件中的所有属性和事件绑定到 v a n t − f i e l d 组件上,使用 v − m o d e l 将 v a l u e 绑定到 v a n t − f i e l d 组件的值上, ∗ ∗ 使用 v − o n = " attrs" 将父组件中的所有属性和事件绑定到 vant-field 组件上,使用 v-model 将 value 绑定到 vant-field 组件的值上,**使用 v-on=" attrs"将父组件中的所有属性和事件绑定到vant−field组件上,使用v−model将value绑定到vant−field组件的值上,∗∗使用v−on="listeners” 将父组件中的所有事件监听器绑定到 vant-field 组件上**。
最后,在 vant-field 组件中添加了一个使用 $slots 继承原组件中插槽的 块,遍历 $slots 特殊属性中的所有插槽,并将每个插槽的名称作为 slot 属性绑定到 vant-field 组件中,使用 元素将插槽继承到子组件中。
这样就可以在二次封装的 MyCustomField 组件中继承 vant-field 组件的所有插槽了。我们可以在使用 MyCustomField 组件时,像使用 vant-field 组件一样,添加任意数量的插槽,这些插槽都会被自动继承到 MyCustomField 组件中。同时也包含了所有的事件。