Vue.js结合Pinia异步处理GraphQL数据时的过滤报错问题
解决Pinia中useQuery数据未就绪导致的报错问题
核心问题分析
你把useQuery直接放在Pinia选项式的state里,这不符合Vue Apollo的使用规范——useQuery是组合式API钩子,必须在组件或store的setup函数中调用。同时,filterItems getter在数据未加载时直接访问state.resultItems.result.items,会因result未就绪抛出错误。
解决方案步骤
1. 重构Pinia Store为Setup语法
将选项式store改为setup形式,正确管理Apollo查询状态:
import { defineStore, ref, computed } from "pinia" import { useQuery } from '@vue/apollo-composable' import { ITEMS_QUERY, SKU_QUERY} from '@/graphql/queries' export const useHomePage = defineStore('homeStore', () => { // 初始化商品列表查询 const { result: itemsResult, loading: itemsLoading } = useQuery(ITEMS_QUERY, { itemBrand: "Estera" }) // 初始化SKU查询,复用refetch避免重复创建实例 const { result: skuResult, refetch: refetchSku } = useQuery(SKU_QUERY, { itemName: '' }) // 定义响应式状态 const nameItem = ref('') const activeSurface = ref('') const activeColor = ref('') const imageItem = ref('') const imagesItem = ref('') const windowsSillLength = ref(100) const mainColl = ref(1) // 处理去重后的颜色列表,添加空值安全判断 const filterItems = computed(() => { // 数据未就绪时返回空数组 if (!itemsResult.value?.items) return [] return [...new Map(itemsResult.value.items.map(item => [item.colorName, item])).values()] }) // 获取第一个商品名称,加入空值校验 function getNameItem() { if (!nameItem.value && filterItems.value.length > 0) { nameItem.value = filterItems.value[0].title } return nameItem.value } // 重新获取SKU数据,使用refetch更新查询 async function getSku(nameItemVal, surface, color, imageItemVal, imagesItemVal) { await refetchSku({ itemName: nameItemVal }) activeSurface.value = surface nameItem.value = nameItemVal activeColor.value = color imageItem.value = imageItemVal imagesItem.value = imagesItemVal windowsSillLength.value = 100 mainColl.value = 1 } return { // 查询状态 itemsLoading, // 响应式变量 nameItem, activeSurface, activeColor, imageItem, imagesItem, // 计算属性与方法 filterItems, getNameItem, getSku } })
2. 组件中添加加载状态处理
在模板中判断数据是否加载完成,避免渲染未就绪内容:
<template> <!-- 加载状态提示 --> <div v-if="HomePage.itemsLoading" class="loading">加载中...</div> <!-- 数据就绪后渲染内容 --> <div v-else> <div class="text-3xl leading-[44px] text-left">{{ HomePage.getNameItem() }}</div> <div class="inputs_block"> <div class="name_color mb-4"> <span class="name text-base leading-6 text-black">Цвет:</span> <span class="value text-sm leading-18px text-[#707070] ml-[5px]">{{ HomePage.activeColor }}</span> </div> <div class="flex gap-x-0.5 flex-wrap ml-[-5px]"> <span v-for="color in filterItems" :key="color.colorName" <!-- 用唯一值做key,避免索引问题 --> :class="HomePage.activeColor === color.colorName ? 'border-[#CAA381]' : 'border-white'" @click="HomePage.getSku(color.title, color.surfaceName, color.colorName, color.image, color.morePhoto)" class="group w-[52px] h-[52px] relative flex justify-center items-center -mt-2 mb-2.5 rounded-[7px] border-2 border-solid hover:cursor-pointer hover:border-[#CAA381]" > <img class="rounded w-[42px] h-[42px]" :src="color.colorUrl" alt=""> <p class="group-hover:block bg-[#F5F3F1] hidden absolute text-xs leading-[14px] text-center text-black bottom-[-30px] w-[max-content] z-[1] px-[3px] py-[5px] rounded-lg">{{ color.colorName }}</p> </span> </div> </div> </div> </template> <script setup> import { storeToRefs } from "pinia" import { useHomePage } from '@/stores/HomePageStore' const HomePage = useHomePage() const { filterItems } = storeToRefs(HomePage) </script>
关键改进点
- 规范useQuery使用:在Pinia的setup函数中调用useQuery,直接获取响应式的
result、loading状态,而非将查询对象存入state。 - 空值安全校验:在
filterItems中用可选链itemsResult.value?.items判断数据是否就绪,未就绪时返回空数组避免报错。 - 优化SKU查询逻辑:使用
refetch方法更新查询,避免重复创建useQuery实例,保持状态一致性。 - 加载状态管理:通过
itemsLoading控制渲染流程,用户不会看到未就绪的错误内容。 - v-for key优化:用唯一的
colorName作为循环key,提升渲染性能与正确性。
内容的提问来源于stack exchange,提问作者Kyrylo Brodnikov
相关产品推荐
相关产品推荐

