Vue3中Pinia嵌套数组Ajax请求后v-for不更新的问题
问题:嵌套数组更新后Vue列表不响应
场景还原
我有一个Pinia仓库SheetItemsStore,代码如下:
const sheetItems = ref([]) function getSheetItems() { return axios.get(route('sheet-items.index', {'balance': balance.value.id})).then((response) => { sheetItems.value = response.data }) }
视图组件中,点击按钮会触发更新并重新获取数据:
<script setup> import {UseBalanceStore} from "@/Stores/UseBalanceStore.js"; import {UseSheetItemsStore} from "@/Stores/UseSheetItemsStore.js"; import PrimaryButton from "@/Components/PrimaryButton.vue"; import {UseCategoriesStore} from "@/Stores/UseCategoriesStore.js"; const balanceStore = UseBalanceStore() const sheetItemStore = UseSheetItemsStore() const categoriesStore = UseCategoriesStore() function updateReferenceDate(value) { axios.patch(route('balances.reference-date.update', {balance: balanceStore.balance.id}), {increase: value}).then(function () { balanceStore.getBalance() balanceStore.getGrandTotal() categoriesStore.getCategories() sheetItemStore.getSheetItems() }) } </script> <template> <div class="flex mt-2 mb-2"> <span class="mt-3 mr-2">Peildatum: {{ balanceStore.balance.reference_date }}</span> <PrimaryButton class="mr-2" @click="updateReferenceDate(false)">Maand achteruit</PrimaryButton> <PrimaryButton class="mr-2" @click="updateReferenceDate(true)">Maand Vooruit</PrimaryButton> </div> </template>
虽然sheetItemStore.getSheetItems()已触发且AJAX请求正常,但嵌套数组category.sheet_items中的属性更新后,以下v-for列表未响应更新:
<div v-for="category in sheetItemsStore.sheetItems" :key="category.id" :tabindex="category.id" class="card sheet-items-container"> <div class="flex justify-between card-title"> <span class="font-bold w-full">{{ category.name.toUpperCase() }}</span> <delete-button v-if="editMode" @click="deleteCategory(category)"/> </div> <div class="sheet-items-list"> <sheet-item v-for="sheetItem in category.sheet_items" :key="sheetItem.id" :edit-mode="editMode" :sheet-item="sheetItem" /> </div> </div>
更新发生在category.sheet_items中sheetItem的属性上,Vue监听器可以设置deep: true,Pinia中是否有类似机制?如何让v-for列表正常更新?
解决方案
方案1:强制替换数组引用
Vue响应式系统对数组的完全替换能直接检测到,若当前替换未触发更新,可通过创建新数组打破原引用:
function getSheetItems() { return axios.get(route('sheet-items.index', {'balance': balance.value.id})).then((response) => { // 展开数组生成新引用 sheetItems.value = [...response.data] // 若嵌套对象也需完全更新,可使用深拷贝 // sheetItems.value = JSON.parse(JSON.stringify(response.data)) }) }
方案2:用reactive处理嵌套结构
改用reactive定义数组,能更好地处理嵌套对象的响应式,配合splice方法更新:
import { reactive } from 'vue' const sheetItems = reactive([]) function getSheetItems() { return axios.get(route('sheet-items.index', {'balance': balance.value.id})).then((response) => { // 清空原数组后插入新数据 sheetItems.splice(0, sheetItems.length, ...response.data) }) }
方案3:手动触发响应式更新
若上述方法无效,可手动调用triggerRef强制触发视图更新:
import { ref, triggerRef } from 'vue' const sheetItems = ref([]) function getSheetItems() { return axios.get(route('sheet-items.index', {'balance': balance.value.id})).then((response) => { sheetItems.value = response.data // 手动触发ref的响应式更新 triggerRef(sheetItems) }) }
方案4:确保嵌套对象为响应式
如果API返回的是普通对象,嵌套层级不会自动转为响应式,可在赋值时手动处理:
import { reactive } from 'vue' function getSheetItems() { return axios.get(route('sheet-items.index', {'balance': balance.value.id})).then((response) => { sheetItems.value = response.data.map(category => ({ ...category, // 将每个sheet_item转为响应式对象 sheet_items: category.sheet_items.map(item => reactive(item)) })) }) }
总结
最稳定的方式是每次更新时创建新数组引用,让Vue自动检测变化;若涉及嵌套属性修改,需确保嵌套对象本身是响应式的,或通过手动触发更新强制刷新视图。
内容的提问来源于stack exchange,提问作者Ivar
相关产品推荐
相关产品推荐

