apollo+react实战项目 stepbystep(八)

使用subscription来自动更新页面

1、什么是graphql subscription

subscription就是当客户端某个事件发生时,服务器端自动发送数据到客户端。subscription通常是由websockets协议实现的。与HTTP不同,WebSocket提供全双工通信,可以在客户端和服务器之间进行双向持续对话。使用subscription意味着我们不再像之前的请求-响应循环那样,而是要在客户端与服务器端建立了稳定的链接,服务器端订阅了一个客户端的事件,一旦客户端事件发生,服务器端便通过已经建立的链接发送数据。

何时使用subscription?

在大多数情况下,间歇性轮询(pollInterval)或手动重新获取(update)实际上是保持客户端最新的最佳方式。那么订阅什么时候才是最佳选择?在以下情况下,订阅特别有用:

 (1)初始状态很大,但增量变化很小。可以使用查询获取起始状态,然后通过订阅更新。

(2)在特定条件下,你可能关心低延迟更新。例如在聊天情况下,用户希望尽快收到新消息。

2、使用apollo 进行subscription

(1)配置apolloClient使用websocket连接进行subscription操作。

使用apolloClient进行订阅前首先要告诉apolloClient订阅的服务器接口端点是什么。我们使用一个名为WebSocketLink的中间件来自apollo-link-ws包来创建websockets链接。

首先要导入apollo-link-ws

yarn add apollo-link-ws

  注意:要使apollo-link-w工作,您还需要安装subscriptions-transport-ws

yarn add subscriptions-transport-ws

  在index.js头部引入相关的包:

import { split } from 'apollo-link'
import { WebSocketLink } from 'apollo-link-ws'
import { getMainDefinition } from 'apollo-utilities'

  webSocketLink用来创建一个ws Link

const wsLink = new WebSocketLink({
  uri: `ws://localhost:4000`,
  options: {
    reconnect: true,
    connectionParams: {
      authToken: localStorage.getItem(AUTH_TOKEN),
    }
  }
})

  connectionParams可以自定义一个对象,服务器可以在设置任何订阅之前验证该连接。这里我们使用ws协议代替了http协议来指定端口。这样我们使用WebSocketLink创建了一个wsLink。

  split是一个link路由选择器,接受三个参数,第一个是一个返回true或者false的函数,当为true时选择第二个link,否则选择第三个link。我们判断是否使用wsLink的基础是,客户端发送了query是subsctiption,如果是订阅请求则使用wsLink,

  getMainDefiniion:用来解析请求,检查请求是否为subscription操作

const link = split(
  ({ query }) => {
    const { kind, operation } = getMainDefinition(query)
    return kind === 'OperationDefinition' && operation === 'subscription'
  },
  wsLink,
  authLink.concat(httpLink)
)

  修改ApolloClient

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

  (2)订阅创建新连接

使用Query组件渲染的subscribeToMore,类似于fetchMore可以接受当前组件的查询结果,和订阅的结果,可以将订阅前的查询结果和订阅结果连接在一起然后更新缓存。

LinkList.js

class LinkList extends Component {
  _updateCacheAfterVote = (store, createVote, linkId) => {
    const data = store.readQuery({ query: FEED_QUERY })
  
    const votedLink = data.feed.links.find(link => link.id === linkId)
    votedLink.votes = createVote.link.votes
  
    store.writeQuery({ query: FEED_QUERY, data })
  }

  _subscribeToNewLinks = async () => {
    // ... you'll implement this 🔜
  }

  render() {
    return (
      <Query query={FEED_QUERY}>
        {({ loading, error, data, subscribeToMore }) => {
          if (loading) return <div>Fetching</div>
          if (error) return <div>Error</div>

          this._subscribeToNewLinks(subscribeToMore)
    
          const linksToRender = data.feed.links
    
          return (
            <div>
              {linksToRender.map((link, index) => (
                <Link
                  key={link.id}
                  link={link}
                  index={index}
                  updateStoreAfterVote={this._updateCacheAfterVote}
                />
              ))}
            </div>
          )
        }}
      </Query>
    )
  }
}

  _subscribeToNewLinks:

_subscribeToNewLinks = subscribeToMore => {
  subscribeToMore({
    document: NEW_LINKS_SUBSCRIPTION,
    updateQuery: (prev, { subscriptionData }) => {
      if (!subscriptionData.data) return prev
      const newLink = subscriptionData.data.newLink.node

      return Object.assign({}, prev, {
        feed: {
          links: [newLink, ...prev.feed.links],
          count: prev.feed.links.length + 1,
          __typename: prev.feed.__typename
        }
      })
    }
  })
}

  subscribeToMore接受两个参数,一个是document,代表订阅查询本身,一个是updateQuery,在updateQuery中类似于update,可以根据结果更新存储。

新链接的订阅document

const NEW_LINKS_SUBSCRIPTION = gql`
  subscription {
    newLink {
      node {
        id
        url
        description
        createdAt
        postedBy {
          id
          name
        }
        votes {
          id
          user {
            id
          }
        }
      }
    }
  }
`

  (3)订阅投票

_subscribeToNewVotes = subscribeToMore => {
  subscribeToMore({
    document: NEW_VOTES_SUBSCRIPTION
  })
}

  订阅投票document

const NEW_VOTES_SUBSCRIPTION = gql`
  subscription {
    newVote {
      node {
        id
        link {
          id
          url
          description
          createdAt
          postedBy {
            id
            name
          }
          votes {
            id
            user {
              id
            }
          }
        }
        user {
          id
        }
      }
    }
  }
`

  添加订阅

this._subscribeToNewVotes(subscribeToMore)

  这里我们的link及订阅了投票,也订阅了创建新链接。

除了使用subscribeToMore函数完成订阅,简单的订阅更新还可以使用《Subscription》组件完成。

  

 

posted @ 2018-09-07 11:21  tutu_python  阅读(294)  评论(0)    收藏  举报