Vue3中Vuetify v-data-table开启return-object后全选取消异常
Vuetify v-data-table开启
return-object后取消全选选中数组不清空的问题解决 问题现象
使用Vue3 + Vuetify的v-data-table组件,开启return-object属性后出现异常:
- 首次点击全选,能得到正确的Proxy对象数组(对应处理后的用户项)
- 点击取消全选时,
selected数组未清空,反而包含重复的项(一次是普通对象,一次是Proxy对象)
未开启return-object时功能正常,但业务需要获取完整的用户对象。将computed属性items的值存入ref后,功能恢复正常。
原因分析
问题核心在于computed返回的items每次计算都会生成全新的对象数组。Vuetify的v-data-table在return-object模式下,默认通过对象的引用地址跟踪选中状态。当items是computed属性时,每次响应式依赖(如usersList)变化或组件重新渲染,computed都会返回新的对象,导致组件无法正确关联之前选中的对象引用,取消全选时无法清空selected数组,反而累积了新旧对象。
而将computed的items存入ref后,items的引用保持稳定(除非手动更新),组件能正确跟踪选中状态,因此功能恢复正常。
解决方案
方案1:添加item-key属性(推荐)
Vuetify的v-data-table支持item-key属性,指定唯一标识字段后,组件会通过ID而非对象引用来跟踪选中状态。即使items是computed生成的新对象,也能通过id正确匹配选中项:
<template> <v-data-table v-model="selected" :headers="headers" :items="items" show-select return-object item-key="id" <!-- 新增:指定唯一标识字段 --> ></v-data-table> </template> <script lang="ts" setup> const usersList = ref([ { id: 1, attributes: { name: { firstName: "testFirst", secondName: "testSecond" }, phone: { mobile: "xxxxxxxxxx" }, dates: [{ id: 1, start: "24.03.2024", end: "25.05.2024" }, { id: 2, start: "21.03.2024", end: "23.05.2024" }], }, }, { id: 2, attributes: { name: { firstName: "testFirst", secondName: "testSecond" }, phone: { mobile: "xxxxxxxxxx" }, dates: [{ id: 3, start: "24.06.2024", end: "25.07.2024" }, { id: 4, start: "21.06.2024", end: "23.07.2024" }], }, } ]); const search = ref("search"); const selected = ref([]); const items = computed(() => { if (usersList.value.length) { return usersList.value.map((item) => ({ id: item.id, name: `${item.attributes.name.firstName} ${item.attributes.name.secondName}`, phone: item.attributes.phone.mobile, })); } else { return []; } }); const headers = computed(() => { if (items.value.length) { const keys = Object.keys(items.value[0]); return keys .map((key) => ({ title: "some title", value: key })) .filter((item) => item.value !== "id"); } else { return []; } }); </script>
方案2:将处理后的items存入ref并保持响应式
通过监听usersList的变化,手动更新ref存储的处理后数据,既保证引用稳定,又维持响应式:
<template> <v-data-table v-model="selected" :headers="headers" :items="itemss" show-select return-object ></v-data-table> </template> <script lang="ts" setup> import { ref, watch } from 'vue'; const usersList = ref([ // 你的用户数据 ]); const search = ref("search"); const selected = ref([]); const itemss = ref([]); const headers = ref([]); // 监听usersList变化,更新处理后数据和表头 watch(usersList, (newUsers) => { if (newUsers.length) { itemss.value = newUsers.map((item) => ({ id: item.id, name: `${item.attributes.name.firstName} ${item.attributes.name.secondName}`, phone: item.attributes.phone.mobile, })); // 生成表头 const keys = Object.keys(itemss.value[0]); headers.value = keys .map((key) => ({ title: "some title", value: key })) .filter((item) => item.value !== "id"); } else { itemss.value = []; headers.value = []; } }, { immediate: true }); // 立即执行初始化 </script>
方案3:用缓存保持处理后对象的引用稳定
通过Map缓存处理后的对象,确保相同原始用户项返回相同的对象引用:
<template> <v-data-table v-model="selected" :headers="headers" :items="items" show-select return-object ></v-data-table> </template> <script lang="ts" setup> import { ref, computed } from 'vue'; const usersList = ref([ // 你的用户数据 ]); const search = ref("search"); const selected = ref([]); // 缓存处理后的对象,保证引用稳定 const processedCache = new Map(); const items = computed(() => { if (!usersList.value.length) return []; return usersList.value.map((item) => { if (processedCache.has(item.id)) { return processedCache.get(item.id); } const processedItem = { id: item.id, name: `${item.attributes.name.firstName} ${item.attributes.name.secondName}`, phone: item.attributes.phone.mobile, }; processedCache.set(item.id, processedItem); return processedItem; }); }); const headers = computed(() => { if (!items.value.length) return []; const keys = Object.keys(items.value[0]); return keys .map((key) => ({ title: "some title", value: key })) .filter((item) => item.value !== "id"); }); </script>
内容的提问来源于stack exchange,提问作者Alex
相关产品推荐
相关产品推荐

