Vue3组合式API中如何实现父子组件v-model双向绑定
问题原因
Vue3 组件上的v-model是固定语法糖,<Child v-model="input" />等价于以下写法:
<Child :modelValue="input" @update:modelValue="newVal => input = newVal" />
你写的子组件既没有接收modelValue属性,也没有在值变化时触发update:modelValue事件通知父组件更新,内部的babyInput是完全独立的局部响应式变量,和父组件传入的值没有关联,自然无法实现数据同步。
实现方案
父组件代码不需要做任何调整,你可以根据自己的代码习惯选以下两种子组件写法。
方案1:原生事件绑定实现
子组件直接绑定输入框的value属性和input事件,对接v-model的约定规则:
<!-- 子组件模板 --> <template> <input type="text" :value="modelValue" @input="e => emit('update:modelValue', e.target.value)" /> </template> <script> export default { props: { // 接收父组件v-model传入的值,默认prop名固定为modelValue modelValue: { type: String, default: "" } }, // 声明要触发的更新事件 emits: ["update:modelValue"], setup(props, { emit }) { return {}; }, }; </script>
如果用<script setup>语法,代码可以更精简:
<template> <input type="text" :value="modelValue" @input="e => emit('update:modelValue', e.target.value)" /> </template> <script setup> const props = defineProps({ modelValue: { type: String, default: "" } }) const emit = defineEmits(["update:modelValue"]) </script>
方案2:计算属性适配原有v-model写法
如果你想保留子组件模板里直接给input绑v-model的写法,可以通过计算属性的get/set做一层代理,不需要改动原有模板结构:
<template> <!-- 这里不需要改,和你最开始写的一致 --> <input type="text" v-model="babyInput" /> </template> <script> import { computed } from 'vue' export default { props: { modelValue: { type: String, default: "" } }, emits: ["update:modelValue"], setup(props, { emit }) { const babyInput = computed({ // 读取值时直接取父组件传入的prop get() { return props.modelValue }, // 值修改时触发更新事件通知父组件同步 set(newVal) { emit('update:modelValue', newVal) } }) return { babyInput }; }, }; </script>
避坑提示:不要直接把
props.modelValue赋值给一个普通ref再绑定v-model,普通ref只会在初始化时读取一次prop值,后续父组件值更新不会同步到子组件,子组件修改ref值也不会通知父组件,就会出现你现在遇到的数据不同步问题。
内容的提问来源于stack exchange,提问作者Deji James
相关产品推荐
相关产品推荐

