思路分析:
- 给出双绑定义
- v-model本质
- 双绑带来的好处
- 补充v-model可用在自定义事件(引导面试官提问)
回答范例:
-
双向数据绑定就是绑定一个响应式数据到视图,数据的变化能自动反映在视图中,同时用户的输入也能自动更新数据。
-
v-model
其实是一种语法糖 (就是一种简写方式,最终会被转换成原始写法)。在表单元素上使用,编译器会根据不同的表单元素使用不同的属性并抛出不同的事件:
-
对于
text
和textarea
元素使用value
属性和input
事件 -
对于
checkbox
和radio
元素使用checked
属性和change
事件 -
对于
select
元素使用value
属性和change
事件另外还可以结合
.lazy
,.number
,.trim
对v-model
的行为做进一步限定。
-
总的来说,使用
v-model
能减少大量繁琐的事件处理代码,提高开发效率。 -
另外,
v-model
也可以用于自定义组件中。(等面试官问)
自定义组件的v-model
vue2和vue3在自定义组件使用v-model
原理都是父子组件间通讯 ,只不过代码书写不一样。
vue2自定义组件的v-model
在vue2中,父组件使用v-model
向子组件传递响应式数据,子组件需要在props
配置项中接收,同时需要在相应的元素监听触发事件,像input
、click
等事件,通过触发$emit
将值回传给父组件。
追问:自定义组件使用v-model如果想改变属性名和事件名怎么做?
我们知道默认的属性名是value
,事件名是input
。可通过配置项model
,prop
可命名属性,event
可命名事件。
追问:v-model和传统的使用 props和$emit有什么区别?
v-model
更加简洁,使得双向绑定代码更加清晰和易读,具体表现在对父组件的代码书写上有所简化,父组件在使用子组件时只需要v-model=""
就能传递响应式数据,不用提前定义一个事件处理函数来等待子组件通过emit
修改数据传回到父组件,大致简化了以下代码:
js
<Son :name="obj.name" @mychange="fn"></Son>
let fn=(name)=>{
obj.name=name;
}
追问:v-model和.sync修饰符有什么区别?
相同点:都是语法糖,都是实现父子组件间通讯。
区别:
- 代码书写不同,
v-model="num"
||||:num.sync="num"
- v-model只能绑定父组件的一个响应数据,.sync可绑定多个数据。
<son :a.sync="num1" :b.sync="num2"></son>
我觉得由于vue2的v-model
和.sync
在自定义组件上的使用有点重叠,vue3就对此进行了一定的优化。
vue3自定义组件的v-model
在vue3中,父组件依旧使用v-model
向子组件传递响应式数据,子组件需要接收父组件的props:modelValue
以及方法update:modelValue
,然后通过computed
计算属性的getter
和setter
来实现读取和修改父组件的值,在setter
中,子组件使用update:modelValue
这个emit来修改父组件的值。
追问:vue2和vue3的v-model在自定义组件使用有什么区别?
- vue2可通过
model
配置项来命名属性和事件,vue3删除了model选项,固定使用modelValue
为父组件的props
,使用update:modelValue
这个emit来修改父组件的值。 - vue2的
v-model
只能绑定父组件的一个响应数据,vue3可通过v-model:属性1="" v-model:属性2=""
绑定多个响应数据传给子组件。
追问:有没有听说过defineModel ?
在Vue3.4版本后,出现了defineModel
这个新的api,简化了自定义子组件实现双向绑定大部分操作,可直接通过defineModel
读取和修改父组件的值,我的理解就是相当于做了封装,在底层帮我们实现了defineProps
和defineEmits
。
js
//子组件中
// 声明 "modelValue" prop,由父组件通过 v-model 使用
const model = defineModel()
// 在被修改时,触发 "update:modelValue" 事件
model.value = "hello"
附录代码
由于本文是面试题,在懂代码的基础上,主要进行文字讲述。
以下是vue2和vue3关于自定义组件使用v-model的代码,可进一步回顾:
vue2 父组件
js
<template>
<div>
<myInput v-model="message"></myInput>
<p>{{message}}</p>
</div>
</template>
<script>
import myInput from './components/demo'
export default {
data () {
return {
message:''
};
},
components: {myInput},
methods: {}
}
</script>
子组件
js
<template>
<div>
<input
type="text"
:value="parentData"
@input="updateVal($event.target.value)"
/>
</div>
</template>
<script>
export default {
data() {
return {};
},
model: {
//父组件传过来的响应式值,可随便命名,但必须和props一致
prop: "parentData",
// 随便命名事件,对应下面$emit即可
event: "changeXXX",
},
props: {
parentData: {
type: String,
default: "tom",
},
},
methods: {
updateVal(val) {
this.$emit("changeXXX", val);
},
},
};
</script>
vue3 父组件
js
<childComponent v-model="msg" />
子组件
js
<template>
<input type="text" v-model="newValue" />
</template>
<script setup>
import { computed } from "vue";
//接收父组件的props:modelValue
const props = defineProps({
modelValue: {
type: String,
default: "",
},
});
//接收父组件的方法:update:modelValue
const emit = defineEmits(["update:modelValue"]);
//通过computed计算属性的getter和seter来实现读取和修改父组件的值
let newValue = computed({
get: () => {
return props.modelValue;
},
set: (value) => {
emit("update:modelValue", value);
},
});
</script>
参考文章
写在最后
努力准备面试题中,希望整理的信息对您有所帮助,喜欢的话请帮忙点赞。
如有错误还请批评指教,谢谢!