面试官:v-model的原理是什么?

69 阅读1分钟

原理

通过 :value 和 @input 事件实现的

// 在书写方面这样写
<!-- Parent.vue -->
<MyComponent v-model:title="bookTitle"  />

<!-- MyComponent.vue -->
<script setup>
defineProps({
  title: {
    required: true
  }
})
defineEmits(['update:title'])
</script>

<template>
  <input
    :value="title"
    @input="$emit('update:title', $event.target.value)"
  />
</template>
-----------------------------------
// 父组件中的v-model:title会被编译成
<!-- Parent.vue -->
<MyComponent
  :title="bookTitle"
  @update:title="$event => (bookTitle = $event)"
/>

如你所见,这显得冗长得多。然而,这样写有助于理解其底层机制。(摘自Vue官网)

绑定一个prop都如此冗长,如果绑定多个prop,可想而知.

解决方案

在2023-12-29的时候Vue发布了3.4正式版本,自3.4版本之后,官网推荐v-model在父子组件的双向绑定上推荐使用defineModel()

// 即使有多个prop要绑定
<!-- Parent.vue -->
<MyComponent v-model:title="bookTitle" v-model:content="bookContent"  />

<!-- MyComponent.vue -->
<script setup>
const title = defineModel('title')
const content = defineModel('content')
</script>

<template>
  <input type="text" v-model="title" />
  <input type="text" v-model="content" />
</template>

这样的写法是不是很美观

需注意

defineModel支持一个默认值,但在使用的时候,如果子组件中defineModel中设置了默认值,但是父组件中在初始化prop的时候没有给值,这样会导致父子组件不同步的问题.

const foo = ref() // 未传值
<Child v-model="foo">

<!-- Child.vue -->
const model = defineModel({ default: 0 }) //却给了默认值

如上代码,父组件中foo值为undefined,子组件中为0

如果要自定义更新时间名称,还要传递默认值等配置信息怎么写

<!-- Parent.vue -->
<MyComponent v-model:title="bookTitle" />

<!-- Child.vue -->
<script setup>
const title = defineModel('title', {default: "title", required: true})
</script>
<template>
  <input type="text" v-model="title" />
</template>