Vue 3编辑模态框数据流异常:二次打开编辑无响应问题求解
问题根因
你的代码逻辑存在两个核心问题,导致编辑功能仅首次生效:
- 本地表单数据仅初始化一次。
EditModal.vue中写在<script setup>顶层的const data = Object.assign({}, toRaw(props.data))逻辑,只会在组件首次挂载时执行一次,后续props.data发生变化时,组件内部拷贝的本地表单数据不会同步更新。 - 保存后未重置模态框状态。你在
updateItem方法执行完列表更新后,没有把state.showModal设为false,也没有清空state.currentItem,导致第二次点击其他项的编辑按钮时,v-if="state.currentItem"的判定结果始终为真,EditModal组件不会被销毁重建,初始化拷贝逻辑自然不会重新触发,表单会一直保留第一次编辑的旧数据,看起来就像点击没有反应。
另外你的列表v-for没有绑定key,会增加Vue列表节点复用时的异常概率,属于不规范写法。
你一开始为了避免直接修改原始数据做prop拷贝的思路是完全正确的,问题出在拷贝逻辑的执行时机和状态重置缺失上。
修复方案
1. 修复App.vue状态逻辑
首先补全保存后的状态重置,同时给v-for加上唯一key:
<script setup> import { reactive } from 'vue'; import EditModal from './components/EditModal.vue' import ListItem from './components/ListItem.vue' const state = reactive({ items: [ { id: 1, title: 'Item 1' }, { id: 2, title: 'Item 2' } ], currentItem: null, showModal: false }); function editItem(itemData) { state.showModal = true; state.currentItem = itemData; } function updateItem(itemData) { state.items = state.items.map(item => item.id === itemData.id ? itemData : item); // 保存后关闭模态框、清空当前选中项 state.showModal = false; state.currentItem = null; } </script> <template> <div> <EditModal v-if="state.currentItem" :show="state.showModal" :data="state.currentItem" @save="itemData => updateItem(itemData)" /> <ListItem v-for="itemData in state.items" :key="itemData.id" :data="itemData" @edit="itemData => editItem(itemData)" /> </div> </template>
2. 优化EditModal.vue的数据同步逻辑
为了避免后续状态调整(比如加取消按钮、模态框做缓存不销毁)时再次出现数据不同步问题,增加对props.data的监听,每次传入新的编辑项时自动同步更新本地表单副本,同时补全emits声明:
<script setup> import { reactive, toRaw, watch } from 'vue'; const props = defineProps(['show', 'data']); const emit = defineEmits(['save']); // 封装表单初始化方法,支持重复调用 const initForm = () => { // 当前场景是单层数据,浅拷贝即可;如果有嵌套对象,请改用深拷贝: // 方式1:structuredClone(toRaw(props.data)) (现代浏览器原生支持) // 方式2:JSON.parse(JSON.stringify(toRaw(props.data))) (兼容旧环境) return reactive(Object.assign({}, toRaw(props.data))); } const formData = initForm(); // 监听传入的编辑项变化,同步更新本地表单数据 watch(() => props.data, (newVal) => { if (!newVal) return; Object.assign(formData, toRaw(newVal)); }, { immediate: true }) </script> <template> <div v-show="show"> <div class="modal"> <div class="modal-inner"> <input v-model="formData.title"> <button @click="emit('save', formData)">Save</button> </div> </div> </div> </template>
ListItem.vue逻辑本身没有问题,不需要修改。
内容的提问来源于stack exchange,提问作者Timo
相关产品推荐
相关产品推荐

