v-model一般用于表单数据的双向绑定,使用起来也很方便,但是本质上他还是一个语法糖,先拿input输入框举个例子
自定义一个MyInput组件
<template>
<div>
<input
type="text"
:value="value"
@input="$emit('input', $event.target.value)"
/>
</div>
</template>
<script>
export default {
props: {
value: String,
},
methods: {
change() {
this.$emit("input", "hahah");
},
},
};
</script>
创建一个父组件,导入并注册MyInput组件
<template>
<div>
<MyInput :value="value" @input="value = $event"></MyInput>
父组件的value:{{ value }}
</div>
</template>
<script>
import MyInput from "../components/MyInput.vue";
export default {
components: {
MyInput,
},
data() {
return {
value: "",
};
},
};
</script>
看结果
这样我们就实现了一个v-model,本质上就是为input元素的value属性进行数据绑定,然后通过一个input事件将当前文本框的内容发送给父组件,父组件监听一个input事件,将传递过来的值赋值给value,像这样的我们就可以简写成
<MyInput v-model="value"></MyInput>
而在组件上,一个组件上的 v-model 默认会利用名为 value 的 prop 和名为 input 的事件。
我觉得这句话很重要,以往没有仔细看文档这句话,就形成了一种定向思维,要在组件上使用v-model,组件里面必须含有input等这些表单元素才行,其实是大错特错
当我们要使用v-model的时候,只需要在组件中定义一个value的prop,当组件内部需要修改value的值时,自定义input事件传递给父组件,让父组件修改,这样就不违背单向数据流的思想了,因为数据一般都是响应式的嘛
下面举个五星评分组件的例子,就不写的那么完整了,星星用方块代替,点击方块进行评分,并且设置v-model绑定的值是当前评分(就是几颗星),类型是number
- rate.vue
<template>
<div>
<!--
五颗基本星星,循环5的话,那么item就是1,2,3,4,5
通过比较当前item和传进来的value比较,如果当前item小于等于value,
说明当前星星应该是黄色的,绑定一个on的类名,它的css就是背景色为黄色
-->
<span
class="rate"
v-for="(item, index) in 5"
:key="index"
:class="{ on: item <= value }"
></span>
</div>
</template>
<script>
export default {
props: {
value: Number,
},
};
</script>
<style scoped>
.rate {
display: inline-block;
width: 20px;
height: 20px;
margin: 0 10px;
background-color: #666;
}
.on {
background-color: yellow;
}
</style>
- 使用组件
<template>
<div>
<!-- 通过v-model绑定初始评分是2颗星 -->
<Rate v-model="rate"></Rate>
</div>
</template>
<script>
import Rate from "../components/Rate.vue";
export default {
components: {
Rate,
},
data() {
return {
rate: 2,
};
},
};
</script>
效果,目前应该是两颗星是黄色
下面来写点击逻辑,其实就一行
<span
class="rate"
....
@click="$emit('input', item)"
></span>
点击之后,定义一个input事件,参数就是item,父组件就会监听子组件的input事件,将你绑定的值(rate)变成input事件的参数(item),然后数据是响应式的,对应的之就会被修改(变成黄色的星星的数量对应发生变化)
大家也可以看对应的官网
vue3在组件上使用v-model
vue3在组件上的用法发生了变化
value -> modelValue
input -> update:modeVale
那么上面的例子中
<Rate v-model="rate"></Rate>
就等价于
<Rate :model-value="rate" @update:model-value="$event = rate"></Rate>
那么Rate.vue内部就该是
<span
...
@click="$emit('update:modelValue', item)"
></span>
...
props: {
modelValue: Number,
}
...
那么input那些表单元素的绑定本质就是
<input
:value="modelValue"
@input="$emit('update:modelValue', $event.target.value)"
>
- 多值双向绑定
一般来说v-model绑定的就是modelValue,如果你想绑定多个或者绑定其他属性,那就可以给v-model添加参数
v-model:(属性)="(为该属性绑定的值)"
// 当然这个属性应该是子组件的一个prop,且触发方法同v-model,
$emit('update:属性', (参数))
这个是可以和v-model同时存在的
- 子组件
<template>
<div style="marign: 20px">
<h1>子组件</h1>
<div @click="$emit('update:modelValue', '修改了')">{{ modelValue }}</div>
<div @click="$emit('update:message', 'message修改了')">
{{ message }}
</div>
</div>
</template>
<script lang="ts">
import { defineComponent } from "vue";
export default defineComponent({
emits: ["update:modelValue", "update:message"],
props: {
modelValue: String,
message: String,
},
setup() {},
});
</script>
- 使用
<template>
<div>
<Children v-model="value" v-model:message="otherModel"></Children>
<h1>父组件数据</h1>
<div>{{ value }}</div>
<div>{{ otherModel }}</div>
</div>
</template>
<script lang="ts">
import { defineComponent, reactive, toRefs } from "vue";
import Children from "./components/Children.vue";
export default defineComponent({
components: { Children },
setup() {
const state = reactive({
value: "哈哈",
otherModel: "message1",
});
return { ...toRefs(state) };
},
});
</script>
效果就是,点击子组件的文字,两边的文字内容同时修改
|
|
3.x官网链接: