订阅
对于服务器实现,您可以查看 这个简单的示例。
客户端设置
GraphQL 规范没有定义用于发送订阅请求的特定协议。第一个流行的实现 WebSocket 上订阅的 JavaScript 库称为subscriptions-transport-ws。该库不再积极维护。它的继任者是一个名为graphql-ws的库。这两个库不使用相同的 WebSocket 子协议,因此您需要确保您的服务器和客户端都使用相同的库。
Apollo Client 支持graphql-ws和subscriptions-transport-ws。Apollo 文档建议使用较新的库graphql-ws,但如果您需要,这里解释了如何使用两者。
新库:graphql-ws
让我们看看如何使用为最新库 graphql-ws 设置的链接将对该传输的支持添加到 Apollo Client。首先,安装
npm install graphql-ws
然后初始化一个 GraphQL WebSocket 链接
import { GraphQLWsLink } from "@apollo/client/link/subscriptions";
import { createClient } from "graphql-ws";
const wsLink = new GraphQLWsLink(
createClient({
url: "ws://localhost:4000/graphql",
})
);
我们需要根据操作类型使用GraphQLWsLink
或HttpLink
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,请安装
npm install subscriptions-transport-ws
然后初始化一个 GraphQL WebSocket 链接
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
添加到您的智能查询中
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
选项中定义的查询,因此它看起来像这样
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
},
})
如果相关查询已停止,则订阅将自动销毁。
以下是一个示例
// 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
关键字声明 智能订阅
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>
访问订阅。
当服务器支持实时查询并使用订阅来更新它们时,例如 Hasura,您可以将简单订阅用于响应式查询
data () {
return {
tags: [],
};
},
apollo: {
$subscribe: {
tags: {
query: gql`subscription {
tags {
id
label
type
}
}`,
result ({ data }) {
this.tags = data.tags;
},
},
},
},
跳过订阅
如果跳过订阅,它将禁用它,并且它将不再更新。您可以使用skip
选项
// 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
属性
this.$apollo.subscriptions.tags.skip = true
手动添加智能订阅
您可以使用$apollo.addSmartSubscription(key, options)
方法手动添加智能订阅
created () {
this.$apollo.addSmartSubscription('tagAdded', {
// Same options like '$subscribe' above
})
}
提示
在内部,此方法将针对组件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)
},
})
},