一、前言
首先写一个简单的 radio 组件
要实现自定义组件的双向绑定,原理就是父组件将值传递给子组件,子组件监听值的变更然后把值响应出来。
二、实现
在子组件中监听值的变化,然后触发父组件中的自定义事件,变更的值会当做这个自定义组件的返回值
<!-- radio组件(子组件) -->
<template>
<label
class="radio"
v-for="item in options"
:key="item"
@click="toggle(item)"
>
<input
class="radio_type"
type="radio"
:checked="activeVal === item.value"
/>
{{ item.label }}
</label>
</template>
<script>
export default {
props: {
val: {
type: [ String, Number ],
default: '1'
}
},
data() {
return {
options: [
{ label: '男', value: '1' },
{ label: '女', value: '0' },
],
activeVal: this.val,
}
},
watch: {
activeVal(newVal) {
this.$emit('valChange', newVal);
}
},
methods: {
toggle(item) {
this.activeVal = item.value;
}
}
};
</script>
<style lang="less" scoped>
.radio {
display: inline-flex;
align-items: center;
margin-right: 10px;
}
.radio_type {
margin-right: 3px;
}
</style>
父组件中定义一下这个自定义事件,将返回值赋值给绑定的变量。这样就已经实现了自定义组件的双向绑定了。
<!-- 父组件 -->
<template>
<radio :val="sex" @valChange="sexChange" />
</template>
<script>
import Radio from '../components/H-Radio.vue';
export default {
components: {
Radio
},
data() {
return {
sex: '1'
}
},
methods: {
sexChange(value) {
this.sex = value;
}
}
};
</script>
然而这种方式不太便利,在父组件中去双向绑定一个变量,还需要再写一个方法去赋值,那么这个 radio 组件封装的就不够完美,一个组件实现的功能还需要通过其它组件的配合才能完成,这并不符合 低耦合高内聚
这个理念。
三、优化
我们希望的双向绑定是直接在父组件中使用 v-model
去绑定一个变量。
<radio v-model:val="sex" />
可以理解为
<radio :val="sex" @update:val="sex = $event" />
然后在 radio 组件中改变一下写法
watch: {
activeVal(newVal) {
this.$emit('update:val', newVal);
}
}
这种方式就相当于在子组件中去触发这个 update 函数更新 val 的值,在子组件内部就实现了值的变更,不需要再去做一些其他的操作。