前言
相信使用vue开发的同学应该都体会过v-model的便利,它可以非常方便地进行双向数据绑定,只要重新输入内容,视图就会立刻发生改变。本文将着重介绍如何在自定义组件当中使用v-model,以及在vue2和vue3中使用方式上的差异。
概述
v-model是一个语法糖,它在组件使用时相当于如下简写:
// vue2 原生组件
<input v-model="val" />
// 等价于
<input :value="val" @input="val = $event.target.value" />
要让组件的v-model生效,需要接收一个value属性,并在有新的value时触发input事件。以上面代码为例,绑定value属性到名为val的响应式对象,然后在input触发的时候绑定一个函数,每次input的值改变就会更新val,从而实现数据更新。
vue2实现方式
同理,自定义组件要如何支持v-model?先说说vue2的实现思路:
// vue2 自定义组件
<my-component v-model="val" />
// 等价于
<my-component :value="val" @input="val = arguments[0]" />
在MyComponent这个组件上面创建一个v-model,它的实际执行就是value的属性,之后触发input的事件,value接收的值就是事件回调函数的第一个参数。所以在自定义组件中实现事件绑定,我们需要使用$emit去触发input事件。
// MyComponent.vue
<template>
<input type="text" :value="value" @input="updateInput" />
</template>
<script>
export default {
props: {
value: String,
},
methods: {
updateInput(e) {
this.$emit("input", e.target.value);
},
},
};
</script>
以上是常规组件的实现方法,那么面对一些不同寻常的组件又该如何应对呢?
用vue2的方式实现数据绑定的解决方案会出现这么一个问题:vue2的普通组件会默认使用value的属性名和input的事件。但是当在如checked这种单选框、复选框等类型的输入控件可能会将value属性用于不同的目的,不能用来指代当前的状态。如下在使用的属性是checked而非value来表示是否选中,改变的值使用的事件是change而非input,针对这种不走寻常路的组件,vue2的解决方案是添加一个model字段,里面有两个属性,prop表示想要绑定的属性,event表示触发事件的名称。
// BaseCheckbox.vue
<template>
<input type="checkbox" :checked="checked" @change="updateInput">
</template>
<script>
export default {
props: {
checked: Boolean
},
model: {
prop: 'checked',
event: 'change'
},
methods: {
updateInput(e) {
this.$emit('change', e.target.checked)
}
}
}
</script>
// App.vue
<template>
<div id="app">
<my-component v-model="val"></my-component>
<base-checkbox v-model="checked"></base-checkbox>
<h1>{{ val }}</h1>
<h1>{{ checked }}</h1>
</div>
</template>
<script>
import MyComponent from "./components/MyComponent";
import BaseCheckbox from "./components/BaseCheckbox";
export default {
name: "App",
components: {
MyComponent,
BaseCheckbox,
},
data() {
return {
val: '',
checked: false
}
}
};
</script>
vue3实现方式
那么vue2的实现方式是否已经完美的呢?显然不是,它还有以下明显的缺点:
- 繁琐:需要新建model属性
- 只能支持一个v-model:组件中可能会出现需要使用多个v-model双向绑定的场景
- 理解困难:在不同的应用场景(如input输入框和check复选框)需要使用不同的方式
针对这些问题,vue3也给出了新的解决方案:
// vue3
<my-component v-model="foo" />
h(Comp, {
modelValue: foo,
'onUpdate:modelValue': value => (foo = value)
})
直接移除了组件上的model属性,不再使用value和input这两个非常容易混淆的属性和事件,换成了属性名称modelValue和更加详细的事件名称onUpdate:modelValue,换言之,要在vue3的自定义组件中使用v-model,首先需要有modelValue属性,然后需要在更新的时候触发onUpdate:modelValue事件。以下是vue3改造后的代码:
// App.vue
<template>
<div id="app">
<my-component v-model="inputVal"></my-component>
<base-checkbox v-model="checkVal"></base-checkbox>
<h1>{{ inputVal }}</h1>
<h1>{{ checkVal }}</h1>
</div>
</template>
<script>
import { defineComponent, ref } from "vue";
import MyComponent from "./components/MyComponent";
import BaseCheckbox from "./components/BaseCheckbox";
export default defineComponent({
components: {
MyComponent,
BaseCheckbox,
},
setup() {
const inputVal = ref("test");
const checkVal = ref(false);
return {
inputVal,
checkVal,
};
},
});
</script>
// MyComponent.vue
<template>
<input type="text" :value="inputRef.val" @input="updateInput" />
</template>
<script>
import { defineComponent, reactive } from "vue";
export default defineComponent({
props: {
modelValue: String,
},
setup(props, context) {
const inputRef = reactive({
val: props.modelValue || "",
});
const updateInput = (e) => {
const targetVal = e.target.value;
inputRef.val = targetVal;
context.emit("update:modelValue", targetVal);
};
return {
inputRef,
updateInput,
};
},
});
</script>
// BaseCheckbox.vue
<template>
<input type="checkbox" :checked="checkedRef.val" @change="updateCheck" />
</template>
<script>
import { defineComponent, reactive } from "vue";
export default defineComponent({
props: {
checkedValue: Boolean,
},
setup(props, context) {
const checkedRef = reactive({
val: props.checkedValue,
});
const updateCheck = (e) => {
const targetVal = e.target.checked;
checkedRef.val = targetVal;
context.emit("update:modelValue", targetVal);
};
return {
checkedRef,
updateCheck,
};
},
});
</script>
结语
本文讲述了v-model的原理、以及在vue2和vue3中的实现方式,代码的验证可以在codesandbox上来去进行,可以直接选择vue2或者vue3的运行环境,无需本地配置。
标签:vue,自定义,value,vue2,vue3,组件,input,model From: https://www.cnblogs.com/askknow/p/17578993.html