Vue + Pinia数据存储优化:分类页面数据混淆问题咨询
解决方案:Pinia中按查询维度缓存商品数据
问题根源在于现有代码把所有页面数据存入同一个数组,未区分查询条件,导致切换分类后仍能获取到其他场景下的页码数据。无需为每个查询条件单独创建state变量,通过按查询维度分组缓存的方式,既能实现本地数据复用,又能避免数据混淆。
1. 调整State结构
将原有的pages数组改为按查询标识分组的缓存对象,同时新增currentQueryKey记录当前页面使用的查询标识(可选,方便组件统一获取数据):
state: (): ProductState => ({ product: {} as Product, // 缓存结构:{ 查询标识: 该标识下的页面数组 } cache: {} as Record<string, PageableType<Product>[]>, isRequestLoading: false, // 记录当前活跃的查询标识 currentQueryKey: "" }),
2. 定义唯一查询标识
为不同查询场景生成唯一key,确保缓存区分:
- 首页普通商品:
"default" - 指定分类商品:
"category_${categoriesId}" - 促销商品:
"promotion"
复杂筛选场景可将参数序列化为字符串作为key(如"category_1_price_0-100")。
3. 修改Getters
新增根据查询标识和页码获取缓存数据的方法:
getters: { // 根据查询标识+页码获取缓存页面 getCachedProducts(state) { return (queryKey: string, pageNumber: number) => { const pageList = state.cache[queryKey] || []; return pageList.find(page => page.number === pageNumber); }; }, // 获取当前查询标识下的所有页面 currentPageList(state) { return state.cache[state.currentQueryKey] || []; } },
4. 重构Actions加载逻辑
每个加载方法先检查缓存,存在则直接复用,不存在再请求后端并更新缓存:
actions: { async loadProduct(pageNumber: number, pageSize: number) { const queryKey = "default"; // 命中缓存则直接返回 const cachedPage = this.getCachedProducts(queryKey, pageNumber); if (cachedPage) { this.isRequestLoading = false; this.currentQueryKey = queryKey; return; } this.isRequestLoading = true; const res = await ProductActions.loadProduct(pageNumber, pageSize); this.updateCache(queryKey, res); this.currentQueryKey = queryKey; this.isRequestLoading = false; }, async loadProductByCategories(pageNumber: number, pageSize: number, categoriesId: number) { const queryKey = `category_${categoriesId}`; const cachedPage = this.getCachedProducts(queryKey, pageNumber); if (cachedPage) { this.isRequestLoading = false; this.currentQueryKey = queryKey; return; } this.isRequestLoading = true; const res = await ProductActions.loadProductByCategories(pageNumber, pageSize, categoriesId); this.updateCache(queryKey, res); this.currentQueryKey = queryKey; this.isRequestLoading = false; }, async loadProductInPromotion(pageNumber: number, pageSize: number) { const queryKey = "promotion"; const cachedPage = this.getCachedProducts(queryKey, pageNumber); if (cachedPage) { this.isRequestLoading = false; this.currentQueryKey = queryKey; return; } this.isRequestLoading = true; const res = await ProductActions.loadProductInPromotion(pageNumber, pageSize); this.updateCache(queryKey, res); this.currentQueryKey = queryKey; this.isRequestLoading = false; }, // 统一更新缓存方法 updateCache(queryKey: string, payload: PageableType<Product>) { if (!this.cache[queryKey]) { this.cache[queryKey] = []; } // 避免重复添加相同页码,支持更新旧数据 const existingIndex = this.cache[queryKey].findIndex(page => page.number === payload.number); if (existingIndex !== -1) { this.cache[queryKey][existingIndex] = payload; } else { this.cache[queryKey].push(payload); } } }
5. 组件中使用示例
<template> <div v-if="!isRequestLoading"> <ProductItem v-for="product in currentPageList.find(p => p.number === currentPage)?.content || []" :key="product.id" :product="product" /> </div> </template> <script setup> import { storeToRefs } from 'pinia'; import { storeModule } from '@/stores/product'; import { ref, onMounted } from 'vue'; import { useRoute } from 'vue-router'; const route = useRoute(); const productStore = storeModule(); const { isRequestLoading, currentPageList } = storeToRefs(productStore); const currentPage = ref(0); onMounted(() => { // 加载当前分类下的商品 productStore.loadProductByCategories(currentPage.value, 10, route.params.categoryId); }); </script>
额外优化建议
- 给缓存添加过期时间:在state中记录缓存时间戳,加载时判断是否过期,避免长期保留旧数据。
- 分维度管理加载状态:将
isRequestLoading改为loadingStates: Record<string, boolean>,避免切换查询时加载状态混乱。
内容的提问来源于stack exchange,提问作者Nicolas Bayer
相关产品推荐
相关产品推荐

