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

Nuxt3项目中Pinia Store SSR数据获取的问题及解决方案咨询

Pinia Store SSR 水化不匹配及客户端数据不更新问题

需求

创建一个Pinia Store负责数据的缓存与修改,组件仅需调用Store获取数据,无需关心数据来源。

现有代码

vercelStore.ts

export const useVercelStore = defineStore('VercelStore', {
  state: () => ({
    stats: {} as any,
  }),
  actions: {
    async fetchStats(ssr: boolean = true) {
      // 若数据已存在且TTL有效则不获取
      if (this.stats && this.stats.ttl > new Date()) {
        return
      }

      const { data, error } = useCustomFetch('/status/vercel', {
        server: ssr,
      })
      if (data.value) {
        this.stats = {
          ttl: setTTL(5),
          data: data.value,
        }
      }
      if (error.value) {
        this.stats = {
          ttl: setTTL(2),
          data: null,
        }
      }
    },
  },
})

组件代码

const vercelStore = useVercelStore()
const { fetchStats } = vercelStore
const vercelData = computed<UsageData | null>(() => vercelStore.stats.data)
await fetchStats()

存在的问题

  • 当ssr=true时,数据加载正常,但出现水化不匹配错误:

    rendered on server:

    • expected on client: Symbol(v-fgt)
  • 当ssr=false时,客户端请求已发送,但Store始终未填充响应数据。

解决方案

问题原因分析

  1. SSR水化不匹配:服务端渲染时Store状态未同步到客户端,导致客户端 hydration 时DOM结构与服务端渲染结果不一致;组件中直接await fetchStats()会打乱 hydration 流程。
  2. 客户端数据不更新:useCustomFetch是异步操作,直接访问data.value可能在请求完成前获取到null,且未等待请求完成就更新状态,导致数据未正确写入Store。

修改后的Store代码

// vercelStore.ts
export const useVercelStore = defineStore('VercelStore', {
  state: () => ({
    // 明确类型,初始化过期状态
    stats: {
      ttl: new Date(0),
      data: null
    } as { ttl: Date; data: UsageData | null },
  }),
  actions: {
    async fetchStats(ssr: boolean = true) {
      // 检查缓存有效性
      if (this.stats.ttl > new Date()) {
        return this.stats.data
      }

      // 等待请求完成,确保获取到最终响应
      const { data, error } = await useCustomFetch('/status/vercel', {
        server: ssr,
      })

      if (data.value) {
        this.stats = {
          ttl: setTTL(5),
          data: data.value,
        }
      } else if (error.value) {
        this.stats = {
          ttl: setTTL(2),
          data: null,
        }
      }

      return this.stats.data
    },
  },
  // 可选:开启客户端持久化,增强缓存能力
  persist: {
    enabled: true,
    strategies: [{ storage: localStorage, paths: ['stats'] }]
  }
})

// 确保setTTL函数正确生成过期时间
function setTTL(minutes: number): Date {
  const now = new Date()
  now.setMinutes(now.getMinutes() + minutes)
  return now
}

修改后的组件代码

// 组件代码
const vercelStore = useVercelStore()
const vercelData = computed<UsageData | null>(() => vercelStore.stats.data)

// 服务端预取数据,确保服务端与客户端初始状态一致
if (process.server) {
  await vercelStore.fetchStats(true)
}

// 客户端挂载后再触发请求,避免打乱hydration流程
onMounted(async () => {
  if (process.client) {
    await vercelStore.fetchStats(false)
  }
})

额外说明

  • 若使用Nuxt框架,需确保Pinia已正确配置SSR支持,框架会自动序列化服务端Store状态到客户端。
  • 水化不匹配的核心解决思路是让服务端与客户端初始状态一致,通过服务端预取数据实现。
  • 客户端请求必须等待挂载完成后执行,避免在 hydration 阶段修改状态导致DOM差异。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.22 16:05:05