# 订阅

# 设置

关于服务端实现,你可以看看 这个简单的示例

要启用基于 websocket 的订阅,需要做一些额外的设置:

npm install --save apollo-link-ws apollo-utilities
import Vue from 'vue'
import { ApolloClient } from 'apollo-client'
import { HttpLink } from 'apollo-link-http'
import { InMemoryCache } from 'apollo-cache-inmemory'
// 新的引入文件
import { split } from 'apollo-link'
import { WebSocketLink } from 'apollo-link-ws'
import { getMainDefinition } from 'apollo-utilities'

import VueApollo from '@vue/apollo-option'

const httpLink = new HttpLink({
  // 你需要在这里使用绝对路径
  uri: 'http://localhost:3020/graphql',
})

// 创建订阅的 websocket 连接
const wsLink = new WebSocketLink({
  uri: 'ws://localhost:3000/subscriptions',
  options: {
    reconnect: true,
  },
})

// 使用分割连接的功能
// 你可以根据发送的操作类型将数据发送到不同的连接
const link = split(
  // 根据操作类型分割
  ({ query }) => {
    const definition = getMainDefinition(query)
    return definition.kind === 'OperationDefinition' &&
      definition.operation === 'subscription'
  },
  wsLink,
  httpLink
)

// 创建 apollo 客户端
const apolloClient = new ApolloClient({
  link,
  cache: new InMemoryCache(),
  connectToDevTools: true,
})

// 像之前一样安装 vue 插件
Vue.use(VueApollo)

# 订阅更多

如果你需要更新一个来自订阅的智能查询结果,最好的方式是使用 subscribeToMore 查询方法。它将创建链接到智能查询的 智能订阅。你只需要将 subscribeToMore 添加到智能查询中:

apollo: {
  tags: {
    query: TAGS_QUERY,
    subscribeToMore: {
      document: gql`subscription name($param: String!) {
        itemAdded(param: $param) {
          id
          label
        }
      }`,
      // 传递给订阅的变量
      // 由于我们使用了函数,因此它们是响应式的
      variables () {
        return {
          param: this.param,
        }
      },
      // 变更之前的结果
      updateQuery: (previousResult, { subscriptionData }) => {
        // 在这里用之前的结果和新数据组合成新的结果
      },
    }
  }
}

TIP

注意,你可以将一组订阅传递给 subscribeToMore 以将此查询关联到多个订阅。

# Alternate usage

你可以使用 this.$apollo.queries.<name> 访问你在 apollo 选项中定义的查询,所以它看起来像这样:

this.$apollo.queries.tags.subscribeToMore({
  // GraphQL 文档
  document: gql`subscription name($param: String!) {
    itemAdded(param: $param) {
      id
      label
    }
  }`,
  // 传递给订阅的变量
  variables: {
    param: '42',
  },
  // 变更之前的结果
  updateQuery: (previousResult, { subscriptionData }) => {
    // 在这里用之前的结果和新数据组合成新的结果
  },
})

如果相关查询停止,订阅将自动销毁。

这里是一个示例:

// 订阅的 GraphQL 文档
const TAG_ADDED = gql`subscription tags($type: String!) {
  tagAdded(type: $type) {
    id
    label
    type
  }
}`

// SubscribeToMore 标签
// 我们有不同类型的标签
// 每种类型都有一个订阅 '频道'
this.$watch(() => this.type, (type, oldType) => {
  if (type !== oldType || !this.tagsSub) {
    // 我们需要在重新订阅之前取消订阅
    if (this.tagsSub) {
      this.tagsSub.unsubscribe()
    }
    // 在查询上订阅
    this.tagsSub = this.$apollo.queries.tags.subscribeToMore({
      document: TAG_ADDED,
      variables: {
        type,
      },
      // 变更之前的结果
      updateQuery: (previousResult, { subscriptionData }) => {
        // 如果我们在没有做操作的情况下已经添加了标签
        // 这可能是由 addTag 变更上的 `updateQuery` 导致
        if (previousResult.tags.find(tag => tag.id === subscriptionData.data.tagAdded.id)) {
          return previousResult
        }

        return {
          tags: [
            ...previousResult.tags,
            // 添加新的标签
            subscriptionData.data.tagAdded,
          ],
        }
      },
    })
  }
}, {
  immediate: true,
})

# 简单订阅

WARNING

如果要使用订阅的结果更新查询,请使用 subscribeToMore。 以下的方法适用于 'notify' 用例

你可以在 apollo 选项中使用 $subscribe 关键字来声明 智能订阅

apollo: {
  // 订阅
  $subscribe: {
    // 当添加一个标签时
    tagAdded: {
      query: gql`subscription tags($type: String!) {
        tagAdded(type: $type) {
          id
          label
          type
        }
      }`,
      // 响应式变量
      variables () {
        // 像常规查询一样运作
        // 在每次改变值时都会使用正确的变量重新订阅
        return {
          type: this.type,
        }
      },
      // 结果钩子
      // 不要忘记对 `data` 进行解构
      result ({ data }) {
        console.log(data.tagAdded)
      },
    },
  },
},

你可以使用 this.$apollo.subscriptions.<name> 访问这个订阅。

TIP

和查询一样,你可以 使用函数 声明订阅,并且可以 使用响应式函数 声明 query 选项。

# 跳过订阅

如果订阅被跳过,它将被禁用且不再被更新。你可以使用 skip 选项:

// Apollo 具体选项
apollo: {
  // 订阅
  $subscribe: {
    // 当添加一个标签时
    tags: {
      query: gql`subscription tags($type: String!) {
        tagAdded(type: $type) {
          id
          label
          type
        }
      }`,
      // 响应式变量
      variables () {
        return {
          type: this.type,
        }
      },
      // 结果钩子
      result (data) {
        // 更新本地数据
        this.tags.push(data.tagAdded)
      },
      // 跳过这个订阅
      skip () {
        return this.skipSubscription
      }
    },
  },
},

在这里,当 skipSubscription 组件属性改变时,skip 将被自动调用。

你也可以直接访问订阅并设置 skip 属性:

this.$apollo.subscriptions.tags.skip = true

# 手动添加智能订阅

你可以使用 $apollo.addSmartSubscription(key, options) 方法手动添加智能订阅:

created () {
  this.$apollo.addSmartSubscription('tagAdded', {
    // 选项同 '$subscribe'
  })
}

TIP

组件 apollo 选项中的每个 $subscribe 对象入口都在内部调用此方法。

# 标准 Apollo 订阅

使用 $apollo.subscribe() 方法来创建一个 GraphQL 订阅,当组件被销毁时将自动终止。它不会创建智能订阅。

mounted () {
  const subQuery = gql`subscription tags($type: String!) {
    tagAdded(type: $type) {
      id
      label
      type
    }
  }`

  const observer = this.$apollo.subscribe({
    query: subQuery,
    variables: {
      type: 'City',
    },
  })

  observer.subscribe({
    next (data) {
      console.log(data)
    },
    error (error) {
      console.error(error)
    },
  })
},