跳至内容

订阅

对于服务器实现,您可以查看 这个简单的示例

客户端设置

GraphQL 规范没有定义用于发送订阅请求的特定协议。第一个流行的实现 WebSocket 上订阅的 JavaScript 库称为subscriptions-transport-ws。该库不再积极维护。它的继任者是一个名为graphql-ws的库。这两个库不使用相同的 WebSocket 子协议,因此您需要确保您的服务器和客户端都使用相同的库。

Apollo Client 支持graphql-wssubscriptions-transport-ws。Apollo 文档建议使用较新的库graphql-ws,但如果您需要,这里解释了如何使用两者。

新库:graphql-ws

让我们看看如何使用为最新库 graphql-ws 设置的链接将对该传输的支持添加到 Apollo Client。首先,安装

bash
npm install graphql-ws

然后初始化一个 GraphQL WebSocket 链接

js
import { GraphQLWsLink } from "@apollo/client/link/subscriptions";
import { createClient } from "graphql-ws";

const wsLink = new GraphQLWsLink(
  createClient({
    url: "ws://localhost:4000/graphql",
  })
);

我们需要根据操作类型使用GraphQLWsLinkHttpLink

js
import { HttpLink, split } from "@apollo/client/core"
import { GraphQLWsLink } from "@apollo/client/link/subscriptions"; // <-- This one uses graphql-ws
import { getMainDefinition } from "@apollo/client/utilities"

// Create an http link:
const httpLink = new HttpLink({
  uri: "http://localhost:3000/graphql"
})

// Create a GraphQLWsLink link:
const wsLink = new GraphQLWsLink(
  createClient({
    url: "ws://localhost:5000/",
  })
);

// using the ability to split links, you can send data to each link
// depending on what kind of operation is being sent
const link = split(
  // split based on operation type
  ({ query }) => {
    const definition = getMainDefinition(query)
    return (
      definition.kind === "OperationDefinition" &&
      definition.operation === "subscription"
    )
  },
  wsLink,
  httpLink
)

// Create the apollo client with cache implementation.
const apolloClient = new ApolloClient({
  link,
  cache: new InMemoryCache(),
});

apollo 客户端是将提供给 vue 应用程序的客户端,有关更多详细信息,请参见 设置部分

现在,查询和变异将像往常一样通过 HTTP 进行,但订阅将通过 WebSocket 传输完成。

旧库:subscriptions-transport-ws

如果您需要使用 subscriptions-transport-ws,因为您的服务器仍然使用该协议,而不是安装 graphql-ws,请安装

bash
npm install subscriptions-transport-ws

然后初始化一个 GraphQL WebSocket 链接

js
import { WebSocketLink } from "@apollo/client/link/ws" // <-- This one uses subscriptions-transport-ws

const wsLink = new WebSocketLink({
  uri: `ws://localhost:5000/`,
  options: {
    reconnect: true
  }
})

其余配置(创建 httpLink 和链接)与上面为 graphql-ws 描述的相同。

订阅更多

如果您需要从订阅更新智能查询结果,最好的方法是使用subscribeToMore智能查询方法。它将创建 智能订阅,这些订阅与智能查询相关联。只需将subscribeToMore添加到您的智能查询中

js
apollo: {
  tags: {
    query: TAGS_QUERY,
    subscribeToMore: {
      document: gql`subscription name($param: String!) {
        itemAdded(param: $param) {
          id
          label
        }
      }`,
      // Variables passed to the subscription. Since we're using a function,
      // they are reactive
      variables () {
        return {
          param: this.param,
        }
      },
      // Mutate the previous result
      updateQuery: (previousResult, { subscriptionData }) => {
        // Here, return the new result from the previous with the new data
      },
    }
  }
}

提示

请注意,您可以将订阅数组传递给subscribeToMore,以订阅此查询上的多个订阅。

备用用法

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

js
this.$apollo.queries.tags.subscribeToMore({
  // GraphQL document
  document: gql`subscription name($param: String!) {
    itemAdded(param: $param) {
      id
      label
    }
  }`,
  // Variables passed to the subscription
  variables: {
    param: '42',
  },
  // Mutate the previous result
  updateQuery: (previousResult, { subscriptionData }) => {
    // Here, return the new result from the previous with the new data
  },
})

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

以下是一个示例

js
// Subscription GraphQL document
const TAG_ADDED = gql`subscription tags($type: String!) {
  tagAdded(type: $type) {
    id
    label
    type
  }
}`

// SubscribeToMore tags
// We have different types of tags
// with one subscription 'channel' for each type
this.$watch(() => this.type, (type, oldType) => {
  if (type !== oldType || !this.tagsSub) {
    // We need to unsubscribe before re-subscribing
    if (this.tagsSub) {
      this.tagsSub.unsubscribe()
    }
    // Subscribe on the query
    this.tagsSub = this.$apollo.queries.tags.subscribeToMore({
      document: TAG_ADDED,
      variables: {
        type,
      },
      // Mutate the previous result
      updateQuery: (previousResult, { subscriptionData }) => {
        // If we added the tag already don't do anything
        // This can be caused by the `updateQuery` of our addTag mutation
        if (previousResult.tags.find(tag => tag.id === subscriptionData.data.tagAdded.id)) {
          return previousResult
        }

        return {
          tags: [
            ...previousResult.tags,
            // Add the new tag
            subscriptionData.data.tagAdded,
          ],
        }
      },
    })
  }
}, {
  immediate: true,
})

简单订阅

危险

如果您想使用订阅的结果更新查询,请使用subscribeToMore。以下方法适用于“通知”用例。

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

js
apollo: {
  // Subscriptions
  $subscribe: {
    // When a tag is added
    tagAdded: {
      query: gql`subscription tags($type: String!) {
        tagAdded(type: $type) {
          id
          label
          type
        }
      }`,
      // Reactive variables
      variables () {
        // This works just like regular queries
        // and will re-subscribe with the right variables
        // each time the values change
        return {
          type: this.type,
        }
      },
      // Result hook
      // Don't forget to destructure `data`
      result ({ data }) {
        console.log(data.tagAdded)
      },
    },
  },
},

然后,您可以使用this.$apollo.subscriptions.<name>访问订阅。

提示

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

当服务器支持实时查询并使用订阅来更新它们时,例如 Hasura,您可以将简单订阅用于响应式查询

js
data () {
  return {
    tags: [],
  };
},
apollo: {
  $subscribe: {
    tags: {
      query: gql`subscription {
        tags {
          id
          label
          type
        }
      }`,
      result ({ data }) {
        this.tags = data.tags;
      },
    },
  },
},

跳过订阅

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

js
// Apollo-specific options
apollo: {
  // Subscriptions
  $subscribe: {
    // When a tag is added
    tags: {
      query: gql`subscription tags($type: String!) {
        tagAdded(type: $type) {
          id
          label
          type
        }
      }`,
      // Reactive variables
      variables () {
        return {
          type: this.type,
        }
      },
      // Result hook
      result (data) {
        // Let's update the local data
        this.tags.push(data.tagAdded)
      },
      // Skip the subscription
      skip () {
        return this.skipSubscription
      }
    },
  },
},

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

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

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

手动添加智能订阅

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

js
created () {
  this.$apollo.addSmartSubscription('tagAdded', {
    // Same options like '$subscribe' above
  })
}

提示

在内部,此方法将针对组件apollo选项中$subscribe对象的每个条目调用。

标准 Apollo 订阅

使用$apollo.subscribe()方法订阅 GraphQL 订阅,该订阅将在组件销毁时自动被杀死。它不会创建智能订阅。

js
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)
    },
  })
},