# ApolloQuery

你可以使用 ApolloQuery(或 apollo-query)组件直接在模板中侦听 Apollo 查询。

阅读完本页后,在 API 参考 查看所有可用的选项。

# 使用 gql 标签的查询

这是使用 ApolloQuery 组件的推荐方法。它与 gql 标签使用相同的语法,就像在其他示例中一样:

<template>
  <ApolloQuery
    :query="gql => gql`
      query MyHelloQuery ($name: String!) {
        hello (name: $name)
      }
    `"
    :variables="{ name }"
  >
    <!-- TODO -->
  </ApolloQuery>
</template>

我们将一个函数传递给 query 属性,它将 gql 标签作为参数,因此我们可以直接编写 GraphQL 文档。

上面的例子中还包含将 variables 通过其同名属性传递给查询的功能。

ApolloQuery 的默认插槽中,你可以访问有关被侦听查询的各种插槽数据,比如 result 对象:

<template v-slot="{ result: { loading, error, data } }">
  <!-- Loading -->
  <div v-if="loading" class="loading apollo">Loading...</div>

  <!-- Error -->
  <div v-else-if="error" class="error apollo">An error occured</div>

  <!-- Result -->
  <div v-else-if="data" class="result apollo">{{ data.hello }}</div>

  <!-- No result -->
  <div v-else class="no-result apollo">No result :(</div>
</template>

这是一个完整的示例组件:

<script>
export default {
  data () {
    return {
      name: 'Anne'
    }
  }
}
</script>

<template>
  <div>
    <input v-model="name" placeholder="Enter your name">

    <ApolloQuery
      :query="gql => gql`
        query MyHelloQuery ($name: String!) {
          hello (name: $name)
        }
      `"
      :variables="{ name }"
    >
      <template v-slot="{ result: { loading, error, data } }">
        <!-- Loading -->
        <div v-if="loading" class="loading apollo">Loading...</div>

        <!-- Error -->
        <div v-else-if="error" class="error apollo">An error occured</div>

        <!-- Result -->
        <div v-else-if="data" class="result apollo">{{ data.hello }}</div>

        <!-- No result -->
        <div v-else class="no-result apollo">No result :(</div>
      </template>
    </ApolloQuery>
  </div>
</template>

# 配置标签

如果你没有使用 vue-cli-plugin-apollo (v0.20.0+),则需要配置 vue-loader 来转换字符串模板标签。vue-loader 在底层使用 Bublé 来转换组件模板中的代码。我们需要将 dangerousTaggedTemplateString 变换添加到 Bublé 以使 gql 起作用。例如,在使用 Vue CLI 时:

// vue.config.js

module.exports = {
  chainWebpack: config => {
    config.module
      .rule('vue')
      .use('vue-loader')
        .loader('vue-loader')
        .tap(options => {
          options.transpileOptions = {
            transforms: {
              dangerousTaggedTemplateString: true,
            },
          }
          return options
        })
  }
}

在原始的 Webpack 配置中,它将如下所示:

module.exports = {
  module: {
    rules: [
      {
        test: /\.vue$/,
        use: [
          {
            loader: 'vue-loader',
            options: {
              transpileOptions: {
                transforms: {
                  dangerousTaggedTemplateString: true
                }
              }
            }
          }
        ]
      },

      /* 其他规则 ... */
    ]
  }
}

# 使用 gql 文件的查询

使用本组件的另一种方法是创建单独的 .gql 文件。这些文件需要使用 graphql-tag 进行预处理。

<template>
  <ApolloQuery
    :query="require('../graphql/HelloWorld.gql')"
    :variables="{ name }"
  >
    <template v-slot="{ result: { loading, error, data } }">
      <!-- Loading -->
      <div v-if="loading" class="loading apollo">Loading...</div>

      <!-- Error -->
      <div v-else-if="error" class="error apollo">An error occured</div>

      <!-- Result -->
      <div v-else-if="data" class="result apollo">{{ data.hello }}</div>

      <!-- No result -->
      <div v-else class="no-result apollo">No result :(</div>
    </template>
  </ApolloQuery>
</template>

# 查询操作

你可以使用 query 插槽属性来访问智能查询对象。下面是使用 fetchMore 分页数据的示例组件:

<template>
  <ApolloQuery
    :query="/* query */"
    :variables="{
      limit: $options.pageSize
    }"
    v-slot="{ result: { loading, error, data }, query }"
  >
    <!-- 在这里显示数据 -->
    <button v-if="hasMore" @click="loadMore(query)">Load more</button>
  </ApolloQuery>
</template>

<script>
export default {
  pageSize: 10,

  data: {
    return {
      page: 1,
      hasMore: true
    }
  },

  methods: {
    async loadMore (query) {
      await query.fetchMore({
        variables: {
          offset: this.page++ * this.$options.pageSize
        },
        updateQuery: (prev, { fetchMoreResult }) => {
          if (!fetchMoreResult || fetchMoreResult.product.length === 0) {
            this.hasMore = false
            return prev
          }
          return Object.assign({}, prev, {
            product: [...prev.product, ...fetchMoreResult.product]
          })
        }
      })
    }
  }
}
</script>

API 参考 查看所有可用的智能查询方法。

# 使用片段

片段可用于在其他文档中共享部分 GraphQL 文档,以便一致地检索相同的数据,且无需复制粘贴代码。

假设我们有一个GetMessages 查询,带有一个类型是 Message 对象数组的 messages 字段:

query GetMessages {
  messages {
    id
    user {
      id
      name
    }
    text
    created
  }
}

我们想要将 messagesMessage 类型的所有字段提取到一个片段中,以使我们可以在其他地方重复使用它。

首先将 gql 标签导入组件:

import gql from 'graphql-tag'

然后,在组件定义中,声明一个新的 fragments 对象:

export default {
  fragments: {
    /** TODO */
  }
}

以下是应用于 Message 类型的 message 片段,如下所示:

fragment message on Message {
  id
  user {
    id
    name
  }
  text
  created
}

我们可以像查询一样使用 gql 标签:

export default {
  fragments: {
    message: gql`
      fragment message on Message {
        id
        user {
          id
          name
        }
        text
        created
      }
    `
  }
}

我们现在可以在组件中使用 this.$options.fragments.message 访问该片段。要在我们的 GetMessages 查询中使用此片段,我们需要使用 GraphQL 的 ... 展开运算符,并将片段放在查询旁边:

gql`
  query GetMessages {
    messages {
      ...message
    }
  }
  ${$options.fragments.message}
`

这将有效地生成 GraphQL 文档(你可以在你的 GraphQL API playground 上尝试):

query GetMessages {
  messages {
    ...message
  }
}
fragment message on Message {
  id
  user {
    id
    name
  }
  text
  created
}

那么这里发生了什么?GraphQL 将找到 ... 运算符,表明我们将在查询的 messages 字段中选择字段。... 运算符之后是片段名称 message,然后在整个 GraphQL 文档中查找片段。由于我们已经正确定义了片段,因此它能够在查询下面被找到。最后,GraphQL 将复制所有片段内容并用它替换 ...message

最后,我们获得最终查询:

query GetMessages {
  messages {
    id
    user {
      id
      name
    }
    text
    created
  }
}
fragment message on Message {
  id
  user {
    id
    name
  }
  text
  created
}

这是一个完整的示例组件:

<!-- MessageList.vue -->
<script>
import gql from 'graphql-tag'

export default {
  fragments: {
    message: gql`
      fragment message on Message {
        id
        user {
          id
          name
        }
        text
        created
      }
    `
  }
}
</script>

<template>
  <ApolloQuery
    :query="gql => gql`
      query GetMessages {
        messages {
          ...message
        }
      }
      ${$options.fragments.message}
    `"
  >
    <!-- 内容... -->
  </ApolloQuery>
</template>

# 重复使用片段

现在我们可以在另一个组件中检索 message 片段:

<!-- MessageForm.vue -->
<script>
import gql from 'graphql-tag'
import MessageList from './MessageList.vue'

export default {
  methods: {
    async sendMessage () {
      await this.$apollo.mutate({
        mutation: gql`
          mutation SendMessage ($input: SendMessageInput!) {
            addMessage (input: $input) {
              newMessage {
                ...message
              }
            }
          }
          ${MessageList.fragments.message}
        `,
        variables: {
          /* 这里是变量 */
        },
        /* 这里是其他选项 */
      })
    }
  }
}
</script>