目录
[1 v-model简介](#1 v-model简介)
[2 v-model原理](#2 v-model原理)
[2.1 核心原理](#2.1 核心原理)
[2.2 修饰符](#2.2 修饰符)
[3 v-model快速上手](#3 v-model快速上手)
[3.1 v-model绑定原生表单](#3.1 v-model绑定原生表单)
[3.2 v-model组件数据传递](#3.2 v-model组件数据传递)
[3.2.1 父子组件通信](#3.2.1 父子组件通信)
[3.2.2 v-model实现父子组件数据传递原理](#3.2.2 v-model实现父子组件数据传递原理)
[3.2.3 更便捷的v-model绑定方法](#3.2.3 更便捷的v-model绑定方法)
[3.2.4 总结](#3.2.4 总结)
1 v-model简介
v-model 是Vue框架的一种内置的API指令,用于处理双向数据绑定,本质是一种语法糖写法。
双向数据绑定是指数据绑定在视图上,视图的改变会影响绑定的数据,而数据的改变又会影响视图的显示。因此常用于表单项,表单元素通过v-model绑定数据,数据变化会影响表单元素的数据显示,而表单内的输入数据又会影响绑定的数据。
html
<script setup>
const message = ref(null)
</script>
<template>
<input v-model="message" placeholder="输入内容" />
<p>{{ message }}</p>
</template>


2 v-model原理
2.1 核心原理
上述v-model实际上会被编译器拆解为两个操作:
1.:value的value属性动态绑定。
2.@input输入事件。
即,当input输入框的value发生变化时,事件监听器会执行@input事件绑定的函数,将value的最新值复制到绑定的数据(这里就是message):
html
<input
:value="message"
@input="message = $event.target.value"
>
2.2 修饰符
.lazy:将input事件转为change事件(失焦后更新)
.number:输入值转为数字类型
.trim:自动去除首尾空格
可以为表单项的v-model添加修饰符,从而形成更多的绑定行为。比如使用.lazy和.trim组合,表单元素发生change事件才会进行数据同步,同时自动去除输入框的首尾空格:
html
<input v-model.lazy.trim="text">
3 v-model快速上手
3.1 v-model绑定原生表单
除了可以绑定input表单项,还可以绑定如下表单项:
(1)多行文本
html
<span>Multiline message is:</span>
<p style="white-space: pre-line;">{{ message }}</p>
<textarea v-model="message" placeholder="add multiple lines"></textarea>
**注意,在textarea使用插值表达式是无效的。**比如下面的行为是错误的:
html
<textarea>{{ text }}</textarea>
(2)复选框
单选:
html
<input type="checkbox" id="checkbox" v-model="checked" />
<label for="checkbox">{{ checked }}</label>
javascript
const checked = ref(false)

多选:
html
<div>Checked names: {{ checkedNames }}</div>
<input
type="checkbox"
id="jack"
value="Jack"
v-model="checkedNames"
/>
<label for="jack">Jack</label>
<input
type="checkbox"
id="john"
value="John"
v-model="checkedNames"
/>
<label for="john">John</label>
<input
type="checkbox"
id="mike"
value="Mike"
v-model="checkedNames"
/>
<label for="mike">Mike</label>
javascript
const checkedNames = ref([])

(3)选项框
html
<div>Selected: {{ selected }}</div>
<select v-model="selected">
<option disabled value="">Please select one</option>
<option>A</option>
<option>B</option>
<option>C</option>
</select>
javascript
const selected = ref('')
建议在增加option,属性为disabled和value,即提供一个值为空的禁用选项,该选项会在selected还未有值的时候提供更友好的显示。

3.2 v-model组件数据传递
v-model更常用的用法是父子组件之间的数据传递,在此之前,先来看看常用的两种父子组件的通信方式:父传子(值传递)和子传父(事件传递)。
3.2.1 父子组件通信
(1)父传子
父传子又称为数据下行,父组件通过自定义属性向子组件传递数据,子组件通过props接收:
html
<!-- 父组件 -->
<ChildComponent :title="parentTitle" />
<!-- 子组件 -->
<script setup>
defineProps(['title']) // 接收父组件传递的 title
</script>
这种方式是单向数据流,即子组件不可直接修改title数据。
(2)子传父
子传父又称为事件上行,子组件通过$emit触发事件,父组件监听事件并更新数据:
html
<!-- 子组件 -->
<button @click="$emit('updateTitle', 'New Title')">修改标题</button>
<!-- 父组件 -->
<ChildComponent @updateTitle="parentTitle = $event" />
3.2.2 v-model实现父子组件数据传递原理
而v-model实现父子组件数据传递的原理正是父子组件通信的两种方式的结合:
html
<!-- 父组件 -->
<ChildComponent
:modelValue="data"
@update:modelValue="data = $event"
/>
<!-- 等价于 -->
<ChildComponent v-model="data" />
即,在父组件中使用子组件,通过v-model绑定data数据,底层会被解析为:modelValue的动态值绑定和@update事件函数。
html
<script setup>
defineProps(['modelValue'])
defineEmits(['update:modelValue'])
</script>
<template>
<input
:value="modelValue"
@input="$emit('update:modelValue', $event.target.value)"
/>
</template>
即,在子组件中,通过props接收父组件的modelValue数据,通过emits来监听modelValue的数据修改事件,一旦子组件的modelValue发生变化,就会通过update事件通知父组件修改数据值。
注意:如果不使用v-model进行父子组件的双向数据绑定,仅想父组件传递数据给子组件,在父组件中使用:value="value"来绑定子组件的数据,子组件使用defineProps来接收该数据。
如果子组件通过事件通知父组件,父组件使用@事件="事件函数"绑定子组件,子组件使用defineEmits接收需要监听的事件即可。
3.2.3 更便捷的v-model绑定方法
在Vue 3.4以后,如下方式子组件可以更便捷的获取到父组件的双向数据绑定:
html
<script setup>
const modelValue = defineModel() // 自动关联 Prop 和事件
</script>
<template>
<input v-model="modelValue" />
</template>
3.2.4 总结
v-model支持父子组件的响应式数据和视图的双向绑定,父组件通过modelValue porps数组将响应式数据传递给子组件,子组件视图变化通过@update:modelValue事件将修改后的值传给父组件。数据变了通过动态绑定值影响视图,视图变了通过事件监听影响数据。