使用 Apollo 和 GraphQL 缓存而不是 Vuex?
Posted
技术标签:
【中文标题】使用 Apollo 和 GraphQL 缓存而不是 Vuex?【英文标题】:Using Apollo & GraphQL caching instead of Vuex? 【发布时间】:2020-11-17 08:39:06 【问题描述】:我一直在试验 Apollo 和 GraphQL。我已经读到您可以利用缓存而不是需要 Vuex - 但我无法解决如何做到这一点,而不创建某种将两者结合起来的反模式。
有没有人举例说明他们是如何处理这种情况的?
我正在尝试什么:
使用 Vue Apollo 的 CLI 安装:
vue add apollo
在./vue-apollo.js中设置好配置后,我添加了一个导出到provider和默认选项:
export const defaultOptions =
// ...all the default settings
export function createProvider (options = )
// Create apollo client
const apolloClient, wsClient = createApolloClient(
...defaultOptions,
...options,
)
apolloClient.wsClient = wsClient
// Create vue apollo provider
const apolloProvider = new VueApollo(
defaultClient: apolloClient,
defaultOptions:
$query:
// fetchPolicy: 'cache-and-network',
,
,
errorHandler (error)
// eslint-disable-next-line no-console
console.log('%cError', 'background: red; color: white; padding: 2px 4px; border-radius: 3px; font-weight: bold;', error.message)
,
)
return apolloProvider
在 ./router/index.js 导入 apollo 提供程序、选项和查询...
import createProvider, defaultOptions from '../vue-apollo'
import gql from "graphql-tag"
import homeQuery from '@/queries/home-content.gql'
import pageQuery from '@/queries/page-query.gql'
const client = createProvider(defaultOptions).defaultClient
页面查询功能
const loadPage = (to, from, next) =>
return client.query(
query: pageQuery,
variables:
slug: to.params.slug
)
.then(async ( data ) =>
const pages = data
if (!from.name) store.commit('App/setLoadedToTrue') // Adds loader for app's first/initial load
if (pages.length > 0)
const addPageMutation = gql`
mutation($id: ID!)
addPage(id: $id) @client
`
await client.mutate(
mutation: addPageMutation,
variables: id: pages[0].id
)
next()
else next('/')
)
.catch(err =>
console.log(err)
next('/')
)
首页内容查询功能
const loadHomeData = (to, from, next) =>
const hasHomeContent = client.cache.data.data['HomePage:1']
if (hasHomeContent) next()
else
client.query( query: homeQuery )
.then(async () =>
if (!from.name) store.commit('App/setLoadedToTrue')
next()
)
.catch(err =>
console.log(err)
next('/error')
)
在每个之前:
router.beforeEach((to, from, next) =>
if (!to.hash) NProgress.start()
if (to.name == 'home') return loadHomeData(to, from, next)
if (to.name == 'page') return loadPage(to, from, next)
next()
)
我的突变和解析器
export const storePage = gql`
type page
id: ID!,
title: String!,
title_highlights: String!,
image: UploadFile,
summary: String,
content_block: [ComponentPageContentBlockPageBlock]
type Mutation
addPageMutation(id: ID!): page
`
export const storeHomePage = gql`
type homePage
id: ID!,
bg_words: String,
title: String!,
highlights: String,
body: String,
services: [ComponentHomecontentServices],
profiles: [ComponentProfilesProfiles],
type Mutation
addHomePageMutation: homePage
`
const resolvers =
Mutation:
addCaseStudyMutation: (_, ctx, cache ) =>
const data = cache.readQuery( query: storeCaseStudy )
cache.writeQuery( query: storeCaseStudy, data )
return data
,
addHomePageMutation: (_, ctx, cache ) =>
const data = cache.readQuery( query: storeHomePage )
cache.writeQuery( query: storeHomePage, data )
return data
问题
目前,每个调用都有两个查询文件,一个用于远程,一个用于本地,其中有@client 针对查询。是否可以有一个查询文件,但根据数据是否存在将其设为@client
条件?
我们必须返回带有突变的 readQuery 吗?或者我可以基本上只有addHomePageMutation: (_, ctx, cache ) => cache.writeQuery( query: storeHomepage )
我们需要在变异之前阅读查询吗?我们是否需要将该数据传递给 writeQuery 方法?
最令人困惑的是,我对在不传递变量或改变查询的情况下存储主页数据感到困惑。最后,我只是将其恢复为仅使用“查询”,没有突变,但没有删除解析器。但它仍然有效,数据被缓存为 HomePage:id 根据解析器,并且主页在您每次返回时都引用缓存。这是怎么回事?
这段代码有什么问题吗?
【问题讨论】:
apollo.vuejs.org/guide/local-state.html ? 是的,谢谢。我对文档感到困惑,所以发布了这个期待更多帮助。我们不能只使用 writeData 而不是创建突变和解析器吗?我们每次都需要传递变异变量吗?一切都需要异步吗?人们是否对数据和本地使用重复查询?我希望看到一些处于类似情况的人发布他们的解决方案示例。抱歉,我可能对我的帖子的意图不太清楚。从那以后我已经找到了解决方法,但不确定它是否是最好的解决方案。不过,感谢您的反对票。 useQuery/useMutation 是高级 API,writeQuery/writeData 是在特殊情况下使用的低级 API ...查询和突变并不总是需要 vars(查询可以改变数据,但按照惯例应该是一个突变)...... [真的人们在什么时候可以使用反应? ;)] ...此代码/解决方法是恕我直言,滥用 apollo ...编写解析器而不是减速器,它统一了本地/远程数据访问 谢谢,这很有帮助。我已经更新了我的解决方案并有一些相关的问题。我仍然对突变感到有些困惑。你介意回答这些吗?哈哈,关于 Vue - 我喜欢它。两者我都用过,但个人一直喜欢 Vue,它是 jsx 上的单文件组件。 【参考方案1】:vue - 为什么不 useQuery
在组件中......就像通常 [in react] 一样?使用路由器看起来像个笑话......不可组合......某些子组件如何查询数据 - 道具钻反模式?
目前,每个调用都有两个查询文件,一个用于远程,一个用于本地,其中有@client 针对查询。是否可以有一个查询文件,但根据数据是否存在将其设为@client 条件?
@client 可以作为远程查询结果的一部分(字段)
我们是否必须返回带有突变的 readQuery?或者我可以基本上只有 addHomePageMutation: (_, ctx, cache ) => cache.writeQuery( query: storeHomepage )
突变必须返回一些东西——突变结果的定义类型——它可以用简单的布尔result
字段来定义,突变可以返回true
值。
我们需要在变异之前阅读查询吗?我们是否需要将该数据传递给 writeQuery 方法?
不,这不是必需的,只需传递匹配的结构数据即可。
最令人困惑的是,我对在不传递变量或改变查询的情况下存储主页数据感到困惑。最后,我只是将其恢复为仅使用“查询”,没有突变,但没有删除解析器。但它仍然有效,数据被缓存为 HomePage:id 根据解析器,并且主页在您每次返回时都引用缓存。这是怎么回事?
没有显示代码,不知道
加载代码应该放在本地解析器中; 你应该在你的一个文件组件中使用这段代码有什么问题吗?
useQuery
【讨论】:
谢谢,这很有帮助!我最初只在组件中使用查询。我在路由器中执行此操作的原因允许我在页面导航之前加载页面,并在页面不存在时控制条件,因为它们都是动态的。它具有更流畅的用户体验,而不是加载页面,然后检查页面是否存在,如果不存在则重定向到其他地方;在路线之前加载它也感觉更快。以上是关于使用 Apollo 和 GraphQL 缓存而不是 Vuex?的主要内容,如果未能解决你的问题,请参考以下文章
Apollo GraphQL 客户端查询返回自省结果而不是数据
Apollo 中的缓存正在避免 GraphQL 解决多态关联
Apollo-server-express 返回“无法获取 /”而不是 Graphql 操场
(GraphQL) - 为啥 Apollo 先检查缓存,然后再做其他事情?