You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

在Pinia Store中使用Nuxt Apollo获取产品详情的实现问题

问题根源

你踩了两个核心坑:

  1. getProductData是异步函数,调用后返回的是Promise对象,不是直接的产品数据,Vue的computed属性没法自动等待Promise完成并解析结果,所以页面要么显示Promise的字符串形式,要么因为逻辑判断返回null
  2. 当前的computed写法会在每次依赖变化时重复调用getProductData,导致多次发起请求,同时isLoading的状态会被频繁重置,逻辑完全混乱
修正方案

第一步:重构Pinia Store,用响应式变量存数据

把异步请求的结果存在Store的响应式变量里,而非每次调用都返回Promise:

//using composition api in Pinia
import { ref } from 'vue';
import { defineStore } from 'pinia';

export const useProductStore = defineStore('product', () => {
  const config = useRuntimeConfig();
  const isLoading = ref(false);
  // 新增响应式变量存储产品数据
  const productData = ref(null);

  const getProductData = async (id) => {
    // 优化:如果已加载过该ID的数据,直接返回避免重复请求
    if (productData.value?.product?.id === `gid://shopify/Product/${id}`) {
      return;
    }
    
    isLoading.value = true
    try {
      const query = gql`
        query GetProductsById($id: ID!) {
          product(id: $id) {
            id
            title
            vendor
            publishedAt
            handle
            variants(first: 100) {
              nodes {
                id
                title
                price {
                    amount
                }
                image {
                    url
                    altText
                    width
                    height
                }
              }
            }
          }
        }
      `
      const variables = { 
        id: "gid://shopify/Product/" + id,
      }
      
      const results = await useAsyncQuery(query, variables);
      // 将结果存入响应式变量,而非直接返回
      productData.value = results;
      isLoading.value = false

    } catch (err) {
      console.error('加载产品失败:', err);
      productData.value = null;
      isLoading.value = false
    }
  };

  // 把productData也暴露给外部使用
  return { getProductData, isLoading, productData };
});

第二步:修改页面逻辑,主动触发请求并读取响应式数据

页面不再依赖computed处理异步,而是在路由参数变化时主动调用Store的请求方法,直接读取Store里的响应式数据:

<template>
  <div class="bg-white">
    <div v-if="isLoading">加载中...</div>
    <div v-else>{{ JSON.stringify(productData) }}</div>
  </div>
</template>
  
<script setup>
import { watch, onMounted } from 'vue'
import { storeToRefs } from 'pinia';
import { useProductStore } from '../stores/product-store';
import { useRoute } from 'vue-router'

const route = useRoute();
const productStore = useProductStore();
// 将Store中的响应式变量转为页面可用的ref
const { isLoading, productData } = storeToRefs(productStore);

// 定义加载产品的方法
const loadProduct = async () => {
  const productId = route.query.id;
  if (productId) {
    await productStore.getProductData(productId);
  }
};

// 页面挂载时首次加载产品
onMounted(() => {
  loadProduct();
});

// 监听路由ID变化,切换产品时重新加载
watch(() => route.query.id, () => {
  productData.value = null;
  loadProduct();
});
</script>
关键改动说明
  • Store新增productData响应式变量,异步请求完成后更新它,确保页面能实时响应数据变化
  • 页面通过storeToRefs获取productData的响应式引用,直接在模板中使用
  • 用onMounted和watch主动触发请求,computed仅适合同步计算逻辑,不处理异步
  • 增加重复请求判断,避免同一产品被多次请求

内容的提问来源于stack exchange,提问作者Qiuzman

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.28 20:13:15