使用 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 先检查缓存,然后再做其他事情?

GraphQL:Apollo 客户端缓存如何工作以保持与服务器的数据一致性?

GraphQL + Apollo - 如何强制进行查询,似乎正在缓存一些东西