Nuxt页面首次加载无法渲染Pinia Store产品数组问题求助
问题
基于Nuxt的产品列表页面调用Pinia Store中的fetchProducts()获取数据,首次访问时产品列表无法渲染。从截图可见产品已加载并存于Pinia Store,但页面上的products数组为空;返回首页再进入产品页面则可正常渲染,控制台无报错。
页面代码:
<script setup> import { storeToRefs} from "pinia"; import { useShopStore } from '@/stores/shop' const shop = useShopStore(); shop.fetchProducts(); const {products} = storeToRefs(shop) const hasProducts = computed(() => products.value.length > 0); </script> <template> <NuxtLayout> <div class="container"> <h2>Products</h2> <div class="container" v-if="hasProducts"> <div class="row row-cols-1 gap-3"> <ProductCard :product="product" v-for="product in products" :key="product.id"/> </div> </div> <div v-else> <h3>Products NOT loaded</h3><br> </div> </div> </NuxtLayout> </template>
Pinia Store代码:
import { defineStore } from 'pinia' import Product from "@/models/Product"; export const useShopStore = defineStore('shop', { state: () => ({ products: [] as Product[] }), actions: { fetchProducts: async function () { try { const {data} = await useFetch('https://fakestoreapi.com/products'); //TODO - fix this typescript error `TS2352: Conversion of type 'Ref<unknown>' to type 'Product[]' may be a mistake because neither type sufficiently overlaps with the other.` // @ts-ignore this.products = data as Product[]; } catch (error) { return error } }, } })
排查与修复建议
1. 处理异步调用的状态同步
fetchProducts是异步方法,首次执行时页面不会等待它完成就直接渲染,导致首次渲染时products还是空数组。需要添加加载状态优化渲染逻辑:
- 在Store的state中新增
isLoading标记:
state: () => ({ products: [] as Product[], isLoading: false }), actions: { async fetchProducts() { this.isLoading = true; try { const {data} = await useFetch('https://fakestoreapi.com/products'); this.products = data.value as Product[]; } catch (error) { console.error('加载产品失败:', error); } finally { this.isLoading = false; } } }
- 页面中更新渲染逻辑:
<script setup> // ... const {products, isLoading} = storeToRefs(shop); const hasProducts = computed(() => !isLoading.value && products.value.length > 0); </script> <template> <NuxtLayout> <div class="container"> <h2>Products</h2> <div v-if="isLoading">加载中...</div> <div class="container" v-else-if="hasProducts"> <div class="row row-cols-1 gap-3"> <ProductCard :product="product" v-for="product in products" :key="product.id"/> </div> </div> <div v-else> <h3>Products NOT loaded</h3><br> </div> </div> </NuxtLayout> </template>
2. 修正useFetch的数据赋值
useFetch返回的data是Ref对象,直接赋值会导致Store中存储的是Ref而非实际数组,需取出data.value再赋值:
this.products = data.value as Product[];
3. 调整页面数据加载时机
在Nuxt中,可通过onMounted或useAsyncData确保数据加载完成后再渲染:
- 方案一:用
onMounted调用异步方法
<script setup> import { storeToRefs, onMounted } from "pinia"; import { useShopStore } from '@/stores/shop' const shop = useShopStore(); const {products, isLoading} = storeToRefs(shop); const hasProducts = computed(() => !isLoading.value && products.value.length > 0); onMounted(async () => { await shop.fetchProducts(); }); </script>
- 方案二:结合Nuxt的
useAsyncData
<script setup> import { storeToRefs } from "pinia"; import { useShopStore } from '@/stores/shop' const shop = useShopStore(); const {products, isLoading} = storeToRefs(shop); const hasProducts = computed(() => !isLoading.value && products.value.length > 0); useAsyncData('products', async () => { if (products.value.length === 0) { await shop.fetchProducts(); } }); </script>
4. 修复TypeScript类型错误(可选)
移除@ts-ignore,给useFetch指定返回类型:
const {data} = await useFetch<Product[]>('https://fakestoreapi.com/products'); this.products = data.value || [];
内容的提问来源于stack exchange,提问作者AlexeiP
相关产品推荐
相关产品推荐

