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

Nuxt3中用Pinia存储服务端API初始状态及解决filter报错问题

解决Pinia中数组filter报错及优化数据缓存方案

错误原因

  1. Store中data初始值设为null,组件渲染时restaurants.value仍为null,直接调用filter方法触发Cannot read properties of null (reading 'filter')错误。
  2. callData是异步方法,组件初始化阶段API请求尚未完成,data保持初始null状态,导致同步执行的filter调用失败。

修复步骤

1. 修正Store初始值与API返回处理

将Store中data的初始值改为空数组,并处理API返回可能为空的情况:

// store/restaurants.ts
export const useRestaurantsStore = defineStore(
  "restaurants",
  {
    state: () => ({
      data: [], // 替换null为[]
    }),
    actions: {
      async callData() {
        const { data } = await useFetch('/api/restaurants');
        // 兜底处理避免数据为空
        this.data = data.value?.items || [];
      },
    },
  });

2. 用计算属性处理过滤逻辑

组件中使用computed替代ref,利用响应式自动跟踪数据变化,API返回后自动重新计算过滤结果:

<!-- pages/restaurants/[slug].vue -->
<template>
  <div>
    <!-- 可选:添加加载状态提示 -->
    <div v-if="restaurants.length === 0">加载中...</div>
    <ul class="c-cardList" v-else>
      <li class="c-card" v-for="item in filteredRestaurants" :key="item.id">
        <!-- 用可选链避免深层属性访问报错 -->
        <div class="c-card_name">{{ item.data[0]?.data }}</div>
      </li>
    </ul>
  </div>
</template>
<script setup>
import { computed } from 'vue';
const { data: restaurants } = useRestaurants();
const category = "cafe";

// 计算属性自动响应数据变化
const filteredRestaurants = computed(() => {
  return restaurants.value.filter(element => {
    return element.data[1]?.data[0] === category;
  });
});
</script>

3. 避免重复API请求(可选)

在Composable中判断数据是否已存在,仅当数据为空时调用API:

// composables/useRestaurants.ts
import { storeToRefs } from "pinia";
import { useRestaurantsStore } from '@/store/restaurants';
export default async function () {
  const restaurantsStore = useRestaurantsStore();
  // 已有数据则跳过请求
  if (restaurantsStore.data.length === 0) {
    await restaurantsStore.callData();
  }
  const { data } = storeToRefs(restaurantsStore);
  return { data };
}

组件中需用await调用:

<script setup>
import { computed } from 'vue';
const { data: restaurants } = await useRestaurants();
const category = "cafe";
const filteredRestaurants = computed(() => {
  return restaurants.value.filter(element => element.data[1]?.data[0] === category);
});
</script>

更优实现方案

1. 预处理嵌套数据

针对后端返回的深层嵌套结构,在Store中提前提取所需字段,简化组件逻辑并降低报错风险:

// store/restaurants.ts
actions: {
  async callData() {
    if (this.data.length > 0) return;
    const { data, error } = await useFetch('/api/restaurants');
    if (error.value) {
      console.error('加载餐厅数据失败:', error.value);
      this.data = [];
      return;
    }
    // 预处理数据,扁平化结构
    this.data = data.value?.items.map(item => ({
      id: item.id,
      name: item.data[0]?.data || '未知名称',
      category: item.data[1]?.data[0] || '未知分类'
    })) || [];
  },
},

组件中可直接使用简化后的字段:

<script setup>
const { data: restaurants } = useRestaurants();
const category = "cafe";
const filteredRestaurants = computed(() => {
  return restaurants.value.filter(item => item.category === category);
});
</script>
<template>
  <li class="c-card" v-for="item in filteredRestaurants" :key="item.id">
    <div class="c-card_name">{{ item.name }}</div>
  </li>
</template>

2. 添加加载与错误状态

在Store中维护加载状态和错误信息,提升用户体验:

// store/restaurants.ts
state: () => ({
  data: [],
  isLoading: false,
  error: null
}),
actions: {
  async callData() {
    if (this.data.length > 0) return;
    this.isLoading = true;
    this.error = null;
    try {
      const { data } = await useFetch('/api/restaurants');
      this.data = data.value?.items.map(item => ({
        id: item.id,
        name: item.data[0]?.data || '未知名称',
        category: item.data[1]?.data[0] || '未知分类'
      })) || [];
    } catch (err) {
      this.error = '加载餐厅数据失败,请稍后重试';
      console.error(err);
    } finally {
      this.isLoading = false;
    }
  },
},

组件中使用状态提示用户:

<template>
  <div>
    <div v-if="isLoading">加载中...</div>
    <div v-if="error" class="error">{{ error }}</div>
    <ul class="c-cardList" v-else>
      <li class="c-card" v-for="item in filteredRestaurants" :key="item.id">
        <div class="c-card_name">{{ item.name }}</div>
      </li>
    </ul>
  </div>
</template>
<script setup>
import { computed } from 'vue';
const { data: restaurants, isLoading, error } = useRestaurants();
const category = "cafe";
const filteredRestaurants = computed(() => {
  return restaurants.value.filter(item => item.category === category);
});
</script>

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.08 17:43:19