在 Vue 3.4+ 中,defineModel
是一个宏 API,用于定义组件的模型(model),它使得组件可以方便地通过 v-model
绑定到父组件。然而,在你提供的代码中,存在一些误解和错误使用 defineModel
的情况。
首先,defineModel
宏 API 的作用是定义组件的 props 和 emit 事件,以支持 v-model
的语法糖。它并不会自动处理 prop 的默认值或类型校验。对于默认值,你应该在 props
中直接设置,而对于类型校验,Vue 3 提供了 defineProps
和 defineEmits
宏 API 来分别定义 props 和 emit 事件。
在你的子组件中,你尝试使用 defineModel
来定义一个 count
属性,并且给它指定了类型和默认值。但是,你实际上并没有在 props
中定义 count
,而且 defineModel
并不会设置默认值。因此,count
的值将由父组件通过 v-model
绑定传入。
在父组件中,你通过 v-model="count"
将 count
绑定到子组件。这意味着子组件的 count
prop 的值将会与父组件的 count
ref 同步。因此,当父组件的 count
发生变化时,子组件的 count
prop 也会相应地变化。
关于你提到的打印结果:
- 在子组件中,你尝试对
count
进行一系列加法操作,但此时 count
的值实际上还没有从父组件传入,所以这些操作是没有意义的。 console.log(count.value);
打印的 100
是因为此时 count
的值已经被父组件的 count
ref 同步了。在父组件中,你初始化了 count
为 100
,所以子组件中的 count
prop 也是 100
。nextTick(() => { console.log(count.value); })
打印的 104
是因为你在 nextTick
的回调函数中打印 count.value
。nextTick
会在 DOM 更新后执行回调函数,此时子组件的 count
prop 已经被父组件的 count
ref 同步,并且由于你在子组件中尝试对 count
进行加法操作(尽管这些操作没有意义),所以 count.value
的值变成了 104
。
综上所述,你遇到的输出问题主要是由于对 defineModel
的错误使用以及对 Vue 组件 props 和 v-model
的工作原理理解不足导致的。你应该直接使用 props
和 emit
来处理 v-model
的绑定,而不是使用 defineModel
。
以下是一个修正后的子组件示例:
<script lang="ts" setup>
import { defineProps, defineEmits, nextTick } from "vue";
const props = defineProps({
modelValue: {
type: Number,
default: 13
}
});
const emit = defineEmits(['update:modelValue']);
const count = computed({
get() {
return props.modelValue;
},
set(value) {
emit('update:modelValue', value);
}
});
nextTick(() => {
console.log(count.value); // 这里将会打印从父组件传入的 modelValue 的值
});
</script>
<template>
<div>{{ count }}</div>
</template>
在这个修正后的示例中,我们使用了 defineProps
来定义 modelValue
prop,并使用 defineEmits
来定义 update:modelValue
emit 事件。我们还使用了一个计算属性 count
来封装对 modelValue
的访问和更新,以便能够正确地触发 v-model
的更新逻辑。这样,当父组件的 count
发生变化时,子组件的 count
prop 也会相应地变化,并且子组件可以通过更新 count
来通知父组件进行更新。
众所周知,
v-model
仅仅是语法糖,父组件中的<Context v-model="count" />
将被编译为:那么子组件中的
defineModel
又是什么呢?从上面的代码中可以看出,它应该为子组件定义了一个包含modelValue
的props
和一个自定义事件update:modelValue
。类似于:同时它还有一个返回值,类型为
ModelRef
。它实际上也只是让我们使用更方便而包装的一层代理而已。当我们读取它的值时,它实际上是在访问props.modelValue
;当我们设置它的值时,实际上也只是触发了update:modelValue
事件而已:此时我们的应用中有两个
count
:一个是
Ref
,另一个是ModelRef
。看上去似乎相同,实际上没有任何关系。他们之间实际上是通过父子组件间的props
相联系起来的。让我们回到问题,当子组件中执行
count.value += 1
时,发生了什么:当执行完这一语句时,父组件中的
Ref
确实变成了 101,但是子组件的props
的更新还要等到下一个tick
,所以子组件中的ModelRef
还是 100。所以接下来
count.value += 2
、count.value += 3
、count.value += 4
将会分别把父组件的Ref
设置为 102,103,104。