Vue3中blur触发emit更新对象后表单Tab切换失效问题求助
解决Vue3中子组件blur更新父组件ref导致Tab切换失效的问题
问题原因
你遇到的Tab键切换失效,本质是父组件直接替换了connection对象的引用,导致子组件的props.connection发生引用变化,触发子组件完全重渲染——原来的输入框DOM被销毁重建,自然丢失了焦点。同时子组件内部的a/b/c ref仅在初始化时赋值,未与后续props更新同步,还会引发数据不一致问题。
解决方案
1. 父组件更新对象属性而非替换整个对象
修改父组件的change函数,用Object.assign修改现有对象的属性,保持对象引用不变。这样Vue的响应式系统只会检测到属性变化,不会触发子组件的完全重渲染:
// Parent.vue <script setup> import { ref } from 'vue' import Child from './Child.vue' // 初始化空对象而非null,避免可选链的冗余判断 const connection = ref({}) const change = (updated) => { // 更新现有对象的属性,保留原引用 Object.assign(connection.value, updated) } </script> <template> <Child :connection="connection" @update="change" /> </template>
2. 子组件监听props变化同步内部状态
子组件通过watch监听props.connection的变化,同步内部状态,确保数据与props一致,同时避免重渲染丢失焦点:
// Child.vue <script setup> import { ref, watch } from 'vue' const props = defineProps({ connection: { type: Object, default: () => ({}) // 给默认值,避免可选链判断 }, }) const emit = defineEmits(['update']) // 用单个ref对象管理所有表单字段,减少重复代码 const formData = ref({ ...props.connection }) // 监听connection的属性变化,同步内部表单状态 watch(props.connection, (newConn) => { Object.assign(formData.value, newConn) }, { deep: true, immediate: true }) // deep监听对象属性,immediate确保初始化时同步数据 const update = () => { emit('update', { ...formData.value }) } </script> <template> <input v-model="formData.a" @blur="update" /> <input v-model="formData.b" @blur="update" /> <input v-model="formData.c" @blur="update" /> </template>
方案优势
- 父组件无需处理单个字段,仍保持“子组件统一emit整个对象”的简洁性;
- 保持
connection对象引用不变,子组件不会触发完全重渲染,输入框DOM节点得以保留,焦点状态自然维持; - 通过
watch同步props与内部状态,确保父子组件数据一致性。
内容的提问来源于stack exchange,提问作者Stokedout
相关产品推荐
相关产品推荐

