跳至内容

使用片段

一个 GraphQL 片段 是一个共享的查询逻辑片段。

graphql
fragment NameParts on Person {
  firstName
  lastName
}

query GetPerson {
  people(id: "7") {
    ...NameParts
    avatar(size: LARGE)
  }
}

需要注意的是,on 子句后的组件用于我们从中选择的类型。在本例中,people 的类型为 Person,我们想要从 people(id: "7") 中选择 firstNamelastName 字段。

在 Apollo 中,片段主要有两种用途

  • 在多个查询、变异或订阅之间共享字段。
  • 将查询分解,以便您可以将字段访问与使用它们的位置放在一起。

在本文件中,我们将概述执行这两种操作的模式;我们还将使用 graphql-anywheregraphql-tag 包中的实用程序,这些实用程序旨在帮助我们,尤其是在解决第二个问题时。

重用片段

片段最直接的用途是在应用程序的不同部分重用查询(或变异或订阅)的一部分。例如,在 GitHunt 的评论页面上,我们希望在发布评论后获取与最初查询相同的字段。这样,我们可以确保在数据更改时呈现一致的评论对象。

为此,我们可以简单地共享一个描述我们需要的评论字段的片段

js
import gql from 'graphql-tag'

export const commentFragment = {
  comment: gql`
    fragment CommentsPageComment on Comment {
      id
      postedBy {
        login
        htmlUrl
      }
      createdAt
      content
    }
  `,
}

这可以在我们的组件 <script> 部分或按照惯例在同级 fragments.js 文件中完成。

当需要将片段嵌入查询时,我们只需在 GraphQL 文档中使用 ...Name 语法,并将片段嵌入到我们的查询 GraphQL 文档中

js
const SUBMIT_COMMENT_MUTATION = gql`
  mutation SubmitComment($repoFullName: String!, $commentContent: String!) {
    submitComment(repoFullName: $repoFullName, commentContent: $commentContent) {
      ...CommentsPageComment
    }
  }
  ${commentFragment}
`

export const COMMENT_QUERY = gql`
  query Comment($repoName: String!) {
    # ...
    entry(repoFullName: $repoName) {
      # ...
      comments {
        ...CommentsPageComment
      }
      # ...
    }
  }
  ${commentFragment}
`

您可以在 GitHunt 中查看 CommentsPage 的完整源代码 这里

将片段放在一起

GraphQL 的一个关键优势是响应数据的树状结构,在许多情况下,它反映了您渲染的组件层次结构。这与 GraphQL 对片段的支持相结合,使您可以将查询拆分为多个部分,以便查询获取的各种字段与使用该字段的代码位于同一位置。

虽然这种技术并不总是有效(例如,GraphQL 架构并不总是由 UI 需求驱动的),但当它有效时,可以在 Apollo 客户端中使用一些模式来充分利用它。

在 GitHunt 中,我们在 FeedPage 上展示了这方面的示例,它构建了以下视图层次结构

FeedPage
└── Feed
    └── FeedEntry
        ├── RepoInfo
        └── VoteButtons

FeedPage 执行查询以获取 Entry 列表,每个子组件都需要每个 Entry 的不同子字段。

graphql-anywhere 包为我们提供了工具,可以轻松地构建一个提供每个子组件所需的所有字段的单个查询,并允许轻松地将子组件所需的精确字段传递给它。

创建片段

要创建片段,我们再次使用 gql 帮助程序,例如

js
// VueButtons.vue

export const entryFragment = gql`
  fragment VoteButtons on Entry {
    score
    vote {
      voteValue
    }
  }
`

如果我们的片段包含子片段,那么我们可以将它们传递给 gql 帮助程序

js
import { entryFragment as VoteButtonsEntryFragment } from './VoteButtons.vue'
import { entryFragment as RepoInfoEntryFragment } from './RepoInfo.vue'

export const entryFragment = gql`
  fragment FeedEntry on Entry {
    commentCount
    repository {
      fullName
      htmlUrl
      owner {
        avatarUrl
      }
    }
    ...VoteButtons
    ...RepoInfo
  }
  ${VoteButtonsEntryFragment}
  ${RepoInfoEntryFragment}
`

使用片段进行过滤

我们还可以使用 graphql-anywhere 包在将字段传递给子组件之前从 entry 中过滤出精确的字段。因此,当我们渲染 VoteButtons.vue 时,我们可以简单地执行以下操作

vue
<script>
import { filter } from 'graphql-anywhere'
import { entryFragment as VoteButtonsEntryFragment } from './VoteButtons.vue'

setup () {
  return {
    entry: ...,
    filterVoteButtonEntry: entry => filter(VoteButtonsEntryFragment, entry),
  }
}
</script>

<template>
  <VoteButtons
    :entry="filterVoteButtonEntry(entry)"
  />
</template>

filter() 函数将从 entry 中获取片段定义的精确字段。

在使用 Webpack 时导入片段

使用 graphql-tag/loader 加载 .graphql 文件时,我们可以使用 import 语句包含片段。例如

graphql
#import "./someFragment.graphql"

query getSomething {
  something {
    ...SomethingFragment
  }
}

将使 someFragment.graphql 的内容可用于当前文件。

联合和接口上的片段

默认情况下,Apollo Client 不需要任何关于 GraphQL 架构的知识,这意味着它非常容易设置,可以与任何服务器一起使用,并且支持即使是最庞大的架构。但是,随着您对 Apollo 和 GraphQL 的使用变得更加复杂,您可能会开始在接口或联合上使用片段。以下是一个在接口上使用片段的查询示例

graphql
query {
  allPeople {
    ... on Character {
      name
    }
    ... on Jedi {
      side
    }
    ... on Droid {
      model
    }
  }
}

在上面的查询中,allPeople 返回类型为 Character[] 的结果。JediDroid 都是 Character 的可能具体类型,但在客户端,如果没有关于架构的信息,就无法知道这一点。默认情况下,Apollo Client 的缓存将使用启发式片段匹配器,它假设如果结果包含其选择集中所有字段,则片段匹配,如果缺少任何字段,则不匹配。这在大多数情况下有效,但也意味着 Apollo Client 无法为您检查服务器响应,也无法在您使用 updateupdateQuerywriteQuery 等手动将无效数据写入存储时告诉您。要将这些多态关系告知缓存存储,您需要将 possibleTypes 选项传递给下面的 InMemoryCache

以下部分说明如何将必要的架构知识传递给 Apollo Client 缓存,以便可以准确地匹配联合和接口,并在将结果写入存储之前对其进行验证。

我们建议设置一个构建步骤,将必要的架构信息提取到一个 JSON 文件中,以便在构建缓存时可以从中导入该文件。要设置它,请按照以下步骤操作

  1. 查询您的服务器/架构以获取有关联合和接口的必要信息,并将其写入文件。

阅读有关如何 使用内省查询自动提取 possibleTypes 的文档。或者使用插件 fragment-matcher 用于 graphql-codegen 并将其配置为 apollo client 3

  1. 使用 possibleTypes.json 在构建期间配置您的缓存。然后,将新配置的缓存传递给 ApolloClient 以完成该过程。
js
import { ApolloClient, InMemoryCache, createHttpLink } from '@apollo/client/core'
import possibleTypes from './possibleTypes.json'

const cache = new InMemoryCache({ possibleTypes })
const httpLink = createHttpLink({ uri });

const client = new ApolloClient({
  cache,
  link: httpLink,
})