Vue3组件如何通过DAO获取数据并维持响应式?
Vue3组件通过DAO同步数据状态的响应式问题解决
我希望用数据访问对象(DAO)为Vue3组件获取数据,对应的DAO是Todos.js,Vue组件是List.vue。启动应用时数据能正常加载显示,但删除等数据更新操作无法在视图中体现。我认为这是因为将数据赋值给了Vue组件本地变量,导致响应式丢失。请问如何恢复响应式,让Vue组件通过DAO同步显示数据状态?
代码示例
Todos.js
export default { todos: [{ text: 'yadda yadda', checked: false, }, { text: 'clean up', checked: true, }, ], getAll() { return this.todos; }, deleteTodo(todo) { let elementToDelete = this.todos.find(element => element.text == todo.text); this.todos.splice(this.todos.indexOf(elementToDelete, 1)); }, };
List.vue
<template> <li v-for="todo in todos"> <v-checkbox v-model="todo.checked" :label="todo.text"></v-checkbox> <v-btn @click="deleteTodo(todo, $event)"> Delete </v-btn> </li> </template> <script> import Todos from '../model/Todos' export default { name: 'List', components : { Todos, }, data() { return { todos:Todos.getAll(), } }, methods: { deleteTodo(todo) { Todos.deleteTodo(todo); } } } </script>
问题分析与解决方法
问题根源
你判断的没错——DAO里的todos数组不是Vue的响应式对象,组件初始化时只是把数组的引用赋值给了本地变量,Vue只能追踪到初始的引用,DAO里对数组的修改(比如splice)无法触发Vue的响应式更新。另外你的deleteTodo方法还有个小bug:splice的第二个参数才是删除数量,你写的1放错了位置,应该是this.todos.splice(index, 1)。
方案1:将DAO的todos转为Vue响应式对象(推荐)
直接在DAO里用Vue的reactive把数组包装成响应式对象,这样组件拿到的数组本身就是响应式的,DAO里的任何修改都会自动同步到视图:
修改后的Todos.js:
import { reactive } from 'vue'; export default { todos: reactive([{ text: 'yadda yadda', checked: false, }, { text: 'clean up', checked: true, }, ]), getAll() { return this.todos; }, deleteTodo(todo) { // 改用findIndex直接获取索引,更高效 const targetIndex = this.todos.findIndex(item => item.text === todo.text); if (targetIndex !== -1) { this.todos.splice(targetIndex, 1); // 修复splice参数错误 } }, };
组件代码不需要任何修改,就能实现响应式同步。
方案2:组件内监听DAO数据变化
如果不想改动DAO的结构,可以在组件里用watch监听DAO数据的变化,手动同步到组件的响应式变量:
修改后的List.vue(使用Vue3的Composition API):
<template> <li v-for="todo in todos"> <v-checkbox v-model="todo.checked" :label="todo.text"></v-checkbox> <v-btn @click="deleteTodo(todo)"> Delete </v-btn> </li> </template> <script> import Todos from '../model/Todos' import { ref, watch } from 'vue' export default { name: 'List', setup() { // 用ref包裹初始数据,创建组件内的响应式变量 const todos = ref([...Todos.getAll()]); // 深度监听DAO的todos数组,变化时同步到组件变量 watch( () => Todos.getAll(), (newTodos) => { todos.value = [...newTodos]; }, { deep: true } ); const deleteTodo = (todo) => { Todos.deleteTodo(todo); }; return { todos, deleteTodo }; } } </script>
这个方案需要开启深度监听,性能上不如方案1直接,适合无法修改DAO的场景。
额外建议
用text作为todo的唯一标识有风险,如果出现相同文本的todo,会导致删除错误。建议给每个todo添加唯一id字段,比如:
todos: reactive([{ id: 1, text: 'yadda yadda', checked: false, }, { id: 2, text: 'clean up', checked: true, }, ])
删除时通过id查找:
const targetIndex = this.todos.findIndex(item => item.id === todo.id);
内容的提问来源于stack exchange,提问作者Hellma
相关产品推荐
相关产品推荐

