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

Vue3 SSR集成Apollo Client并发请求数据混乱问题求助

解决方案:无需完全重构,核心是实现请求级别的实例隔离

你遇到的问题本质是全局实例导致的并发请求污染,以及useApolloClient的使用场景限制。不需要完全重构Apollo Client的使用方式,只需要调整实例的创建与传递逻辑,让每个请求拥有独立的Apollo Client和状态管理实例,并打通各场景的实例获取路径。


1. 核心思路:每个请求创建独立实例并挂载到请求上下文

在SSR中,必须为每个HTTP请求创建独立的Apollo Client、状态管理(Pinia/Vuex)实例,并将这些实例挂载到vite-plugin-ssr的pageContext中,确保请求间完全隔离。

示例:创建请求级实例(app.ts)

import { createApp as createVueApp } from 'vue'
import { DefaultApolloClient } from '@vue/apollo-composable'
import { createApolloClient } from './apollo'
import { createPinia } from 'pinia' // 以Pinia为例,Vuex逻辑类似

export async function createApp(pageContext) {
  // 1. 每个请求创建独立的Apollo Client
  const apolloClient = createApolloClient({ ssrMode: true })
  // 2. 每个请求创建独立的状态管理实例
  const pinia = createPinia()

  // 3. 将Apollo Client注入状态管理,方便store中直接调用
  pinia.use(({ store }) => {
    store.apolloClient = apolloClient
  })

  const app = createVueApp({
    // 根组件逻辑
  })

  app.use(pinia)
  // 4. 提供Apollo Client给@vue/apollo-composable,支持组件内useApolloClient调用
  app.provide(DefaultApolloClient, apolloClient)

  // 5. 将实例挂载到pageContext,方便onBeforeRender等钩子直接使用
  pageContext.apolloClient = apolloClient
  pageContext.pinia = pinia

  return { app }
}

2. 各场景的实例获取方式

在onBeforeRender中使用

直接从pageContext取出实例,无需依赖Vue注入:

import { MY_PAGE_QUERY } from './queries'

export async function onBeforeRender(pageContext) {
  const apolloClient = pageContext.apolloClient
  const { data } = await apolloClient.query({ query: MY_PAGE_QUERY })
  return {
    pageProps: { pageData: data }
  }
}

在组件onServerPrefetch中使用

onServerPrefetch属于组件setup生命周期范畴,可直接通过useApolloClient获取当前请求的实例:

<script setup>
import { useQuery, useApolloClient } from '@vue/apollo-composable'
import { onServerPrefetch } from 'vue'
import { MY_COMP_QUERY } from './queries'

const { result } = useQuery(MY_COMP_QUERY)

onServerPrefetch(async () => {
  // 等待查询完成,确保SSR渲染时数据已就绪
  await result.value
})
</script>

在状态管理的异步action中使用

由于每个请求对应独立的store实例,可直接通过store实例上挂载的apolloClient调用:

import { defineStore } from 'pinia'
import { MY_STORE_QUERY } from '../queries'

export const useMyStore = defineStore('myStore', {
  state: () => ({
    list: []
  }),
  actions: {
    async fetchList() {
      // 直接使用当前请求的Apollo Client实例
      const { data } = await this.apolloClient.query({ query: MY_STORE_QUERY })
      this.list = data.items
    }
  }
})

3. 关键注意事项

  • 关闭Apollo Client的浏览器缓存:SSR模式下需开启ssrMode: true,避免客户端缓存干扰服务端渲染。
  • 禁止全局单例:必须放弃SPA中全局的Apollo Client、状态管理实例,改为请求级创建。
  • 避免provideApolloClient全局调用:该方法会将实例设为全局,必然导致并发污染,仅在局部组件临时覆盖时使用(非必要)。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.13 22:55:17