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
相关产品推荐
相关产品推荐

