1.v-model的实现方式
v-model在Vue.js中是一个语法糖,它实际上是:modelValue
和@input
事件的组合。具体来说,v-model做了以下几件事情:
-
初始化绑定:当Vue实例创建时,它会解析模板中的v-model指令,并找到对应的表单元素。然后,它会将该表单元素的value属性与Vue实例中的某个数据属性(如data中的某个属性)进行绑定。这通常是通过Proxy来实现的,以实现对数据属性的劫持和监听。
-
监听输入事件:Vue还会为该表单元素添加一个input事件监听器。当用户在表单元素中输入内容时,会触发input事件,Vue会捕获到这个事件,并获取用户输入的值。
-
更新数据:Vue将用户输入的值更新到之前绑定的数据属性中。由于Vue的数据响应系统,这个数据属性的变化会自动触发视图的更新,从而实现双向数据绑定。
<template>
//编译前
<input v-model='msg'/>
//vue会将其编译为
<input :value='msg' @input='msg = (<HTMLInputElement>$event.target).value'>
</template>
<script lang='ts' setup>
import {ref} from 'vue'
const msg = ref('')
</script>
2.示例说明
一般在开发中我们会把v-model绑定在自定义组件上,以扩展了其灵活性和适用性。
以下是一个自定义组件支持 v-model
的例子:
//MyInput.vue
<template>
<input
:value="modelValue"
@input="emit('update:modelValue', (<HTMLInputElement>$event.target).value)
">
</template>
<script lang='ts' name='MyInput' setup>
defineProps(['modelValue'])
const emit = defineEmits(['update:modelValue'])
</script>
在父组件中使用这个自定义组件时,可以这样写:
//Father.vue
<template>
//编译前
<MyInput v-model="msg"></MyInput>
//vue会将其编译为
<MyInput :modelValue="msg" @update:modelValue="msg = $event"></MyInput>
</template>
<script lang='ts' name='Father' setup>
import MyInput from './MyInput.vue';
import {ref} from 'vue'
const msg = ref('hello')
</script>
注意:
- 对于原生事件,$event就是事件对象(这里对应着MyInput.vue中的$event)。
- 对于自定义事件,$event就是触发事件时,所传递的数据(这里对应着Father.vue中的$event)。
在父组件中,我们还可以使用 v-model
配置来指定自定义的 prop
和 event
名称:
//Father.vue
<template>
//编译前
<MyInput v-model:msg="msg"></MyInput>
//vue会将其编译为
<MyInput :msg="msg" @update:msg="msg = $event"></MyInput>
</template>
<script lang='ts' name='Father' setup>
import MyInput from './MyInput.vue';
import {ref} from 'vue'
const msg = ref('hello')
</script>
这里 v-model:msg
告诉 Vue 使用 msg作为 prop
和 update:msg作为事件名。
//MyInput.vue
<template>
<input
:value="msg"
@input="emit('update:msg', (<HTMLInputElement>$event.target).value)
">
</template>
<script lang='ts' name='MyInput' setup>
defineProps(['msg'])
const emit = defineEmits(['update:msg'])
</script>
这样的用法可以使组件的扩展性更高,还可以为一个自定义组件绑定多个v-model:
//Father.vue
<template>
<MyInput
v-model:username="username"
v-model:password="password"
></MyInput>
</template>
<script lang='ts' name='Father' setup>
import MyInput from './MyInput.vue';
import {ref} from 'vue'
const username = ref('admin')
const password= ref('123456')
</script>
在自定义组件 MyInput.vue
中:
//MyInput.vue
<template>
<input
:value="username"
@input="emit('update:username', (<HTMLInputElement>$event.target).value)
">
<input
:value="password"
@input="emit('update:password', (<HTMLInputElement>$event.target).value)
">
</template>
<script lang='ts' name='MyInput' setup>
defineProps(['username','password'])
const emit = defineEmits(['update:username','update:password'])
</script>
总结来说,v-model
的底层原理主要是通过属性绑定和事件监听来实现双向数据绑定,并且通过自定义组件扩展了其灵活性和适用性。