點燈坊

失くすものさえない今が強くなるチャンスよ

使用 Vue Apollo 建立 Dynamic Subscription

Sam Xiao's Avatar 2019-12-17

Smart Query 與 Smart Subscription 雖然方便,但必須在 apollo 先宣告 Query 也順便定義其 GraphQL,且在 Component 載入時自動執行;若想在某個 Event 下執行 GraphQL,則要改用 Dynamic Query 與 Dynamic Subscription 動態載入 GraphQL。

Version

macOS Catalina 10.15.2
WebStorm 2019.3
Node 13.2.0
Vue CLI 4.1.1
Vue 2.6.10
Vue Apollo 3.0.0-beta.11

Vue Apollo

src/App.vue

<template>
  <div>
    <ul>
      <li v-for="(item, index) in books" :key="index">
        {{ item.title }}:{{ item.price }}
      </li>
    </ul>
  </div>
</template>

<script>
import gql from 'graphql-tag'

let query = gql`
  query {
    books {
      title
      price
    }
  }
`

let subscription = gql`
  subscription {
    bookAdded {
      title
      price
    }
  }
`

let mounted = function() {
  this.$apollo
    .query({ query })
    .then(x => this.books = x.data.books)

  let next = ({ data: { bookAdded }}) => this.books = [...this.books, bookAdded]

  this.$apollo
    .subscribe({ query: subscription })
    .subscribe({ next })
}

export default {
  name: 'app',
  data: () => ({
    books: []
  }),
  mounted
}
</script>

44 行

export default {
  name: 'app',
  data: () => ({
    books: []
  }),
  mounted
}

並沒有在 apollo property 下定義 books smart query,而是改用傳統 books data。

32 行

let mounted = function() {
  this.$apollo
    .query({ query })
    .then(x => this.books = x.data.books)

  let next = ({ data: { bookAdded }}) => this.books = [...this.books, bookAdded]

  this.$apollo
    .subscribe({ query: subscription })
    .subscribe({ next })
}

所有 query 與 subscription 都會動態寫在 mounted hooks 內。

33 行

this.$apollo
  .query({ query })
  .then(x => this.books = x.data.books)

this.$apollo 讀取 Apollo Client,在其 query() 傳入由 query 組成的 object 產生 dynamic query。

由於 query() 回傳為 promise,可自行在 then() 的 callback 中以 side effect 寫入到 booksdata,也可使用 promise 的 catch()finally() 做進一步處理。

14 行

let query = gql`
  query {
    books {
      title
      price
    }
  }
`

定義 dynamic query 所使用的 GraphQL query。

37 行

let next = ({ data: { bookAdded }}) => this.books = [...this.books, bookAdded]

this.$apollo
  .subscribe({ query: subscription })
  .subscribe({ next })

this.$apollo 讀取 Apollo Client,在其 subscribe() 傳入由 query 組成的 object 產生 dynamic subscription。

因為 query 在 dynamic query 已經使用過,因此改用 subscription 取 alias。

不過 subscribe() 並不是回傳 promise,若要回應 subscription 回傳的資料,必須再呼叫一次 subscribe(),並傳入 next() 為 property 的 function 回應 subscription。

可直接在 next() 的 argument 解構 subscription 回傳的資料,然後直接修改 books data。

23 行

let subscription = gql`
  subscription {
    bookAdded {
      title
      price
    }
  }
`

定義 dynamic subscription 所使用的 GraphQL subscription。

Async Await

let mounted = async function() {
  let { data: { books }} = await this.$apollo.query({ query })
  this.books = books

  let next = ({ data: { bookAdded }}) => this.books = [...this.books, bookAdded]

  this.$apollo
    .subscribe({ query: subscription })
    .subscribe({ next })
}

由於 this.$apollo.query() 回傳 promise,因此也可以使用 async await 修改 books data。

Browser

smart000

原本 books data 只有 2 筆資料。

Insomnia

mutation {
  addBook(book: {
    title: "Speaking JavaScript"
    price: 300
  }) {
    title
    price
  }
}

使用 Insomnia 執行 addBook mutation,相當於在其他 client 執行 mutation 觸發 subscription。

smart001

Browser

smart002

原本 books data 自動更新成 3 筆資料。

Conclusion

  • Vue Apollo 讓我在收到 GraphQL subscription 後,不用再重新執行 GraphQL Query,只要實踐 next() 即可自動在背景更新原本資料,非常方便

Sample Code

完整範例可在我的 GitHub 上找到

Reference

Vue Apollo, Standard Apollo Subscribe