面试题:请阐述一下 v-model 的原理
v-model即可以作用于表单元素,又可作用于自定义组件,无论是哪一种情况(vue2, vue3),它都是一个语法糖,最终会生成一个属性和一个事件。
当其作用于表单元素时,vue会根据作用的表单元素类型而生成合适的属性和事件。例如:
- text 和 textarea 元素使用 value property 和 input 事件;
- checkbox 和 radio 使用 checked property 和 change 事件;
- select 字段将 value 作为 prop 并将 change 作为事件。
用于自定义组件
vue2
v-model也可作用于自定义组件,当其作用于自定义组件时,默认情况下,它会生成一个value属性和input事件。
子组件 HelloWorld
这个子组件只是实现一个简单计数器的功能,然后我向上分发的事件名称是update:value。但是vue2如果使用v-model会自动的把这个事件名称给改成input。
<template> <div class="hello"> <button @click="change(value - 1)">-</button> <span>{{value}}</span> <button @click="change(value + 1)">+</button> </div> </template> <script> export default { name: "HelloWorld", props: { value: Number, }, methods: { change(val) { this.$emit("update:value", val); }, }, }; </script> 复制代码
APP 来使用
<HelloWorld :value="inputVal" @update:value="inputVal = $event" /> 等效于 <HelloWorld v-model="inputVal" /> 复制代码
结果:
分析虚拟dom结果
vue2的虚拟dom查看方式是 在mounted中使用this._vnode来进行查看,然后查看componentOptions来查看组件传递的属性。
开发者可以通过组件的model配置来改变生成的属性和事件 修改子组件 HelloWorld
<template> <div class="hello"> <button @click="change(number - 1)">-</button> <span>{{ number }}</span> <button @click="change(number + 1)">+</button> </div> </template> <script> export default { name: "HelloWorld", props: { number: Number, }, model: { prop: "number", // 默认为 value event: "change", // 默认为 input }, methods: { change(val) { this.$emit("change", val); }, }, }; </script> 复制代码
父组件修改
<HelloWorld v-model="inputVal" /> <!-- 等效于 --> <HelloWorld :number="inputVal" @change="data=$event" /> 复制代码
效果
vue3
v-model也可作用于自定义组件,当其作用于自定义组件时,默认情况下,它会生成一个modelValue属性和onUpdate:modelValue事件。
子组件 Comp
<script setup> import { ref, } from 'vue' const props = defineProps({ modelValue: Number }) const emits = defineEmits(['update:modelValue']); const change = (val) => { emits('update:modelValue', val) } </script> <template> <button @click="change(props.modelValue - 1)"> - </button> <span>{{props.modelValue}}</span> <button @click="change(props.modelValue + 1)"> + </button> </template> 复制代码
父组件App
<script setup> import { ref, getCurrentInstance } from 'vue' import Comp from './Comp.vue' const msg = ref(123); const internalInstance = getCurrentInstance(); console.log(internalInstance) </script> <template> <Comp v-model="msg"></Comp> 等效于 <Comp :modelValue="msg" @update:modelValue="msg = $event"></Comp> </template> 复制代码
结果
虚拟dom分析
vue3 的查看虚拟dom的使用方式,是使用``getCurrentInstance`来查看, 里面的children中的props中来查看属性传递
区别
vue2和vue3都又v-model,原理都是生成一个属性和一个事件,但是也存在些区别。
.sync修饰符
vue3中的.sync修饰符是去掉了的,他的功能可以由v-model的参数替代
例如:
<!-- vue2 --> <Comp :title="inputVal" @update:title="inputVal = $event" /> <!-- 简写为 --> <Comp :title.sync="inputVal" /> <!-- vue3 --> <Comp :title="inputVal" @update:title="inputVal = $event" /> <!-- 简写为 --> <Comp v-model:title="inputVal" /> 复制代码
多个v-model
在vue3中允许你写多个v-model,这也是强烈的说明了,v-model就是一个语法糖,只是帮你减少了代码量,仅此而已。vue2不能写多个v-model
<ChildComponent v-model:title="pageTitle" v-model:content="pageContent" /> <!-- 是以下的简写: --> <ChildComponent :title="pageTitle" @update:title="pageTitle = $event" :content="pageContent" @update:content="pageContent = $event" /> 复制代码
##v-model 修饰符
vue2.x是自带的修饰符,但是在3.x的版本中,可以自定义修饰符哦 .在3.x中的修饰符会在当作属性传递给子组件,并且在属性中生成一个modelModifiers的属性。存在这个修饰符就会有对应的修饰符,并且是true,如果没有传递,那就是undefined。
修改Comp子组件
<script setup> import { ref, } from 'vue' const props = defineProps({ modelValue: Number, modelModifiers: { default: () => ({}) } }) const emits = defineEmits(['update:modelValue']); const change = (val) => { // 如果存在修饰符range2,那就多加1,减法就没有效果 if(props.modelModifiers.range2){ val ++; } emits('update:modelValue', val) } </script> <template> <button @click="change(props.modelValue - 1)"> - </button> <span>{{props.modelValue}}</span> <button @click="change(props.modelValue + 1)"> + </button> </template> 复制代码
父组件使用
<Comp v-model.range2="msg"></Comp> 复制代码
效果
vue3测试代码地址: vue2测试代码地址,由于忘记登录,被删除了