GraphQL 漸進學習 09-graphql-apollo-client-vue-客戶端開發

GraphQL 漸進學習 09-graphql-apollo-client-vue-客戶端開發

軟件環境

  • vue > 2.5.x

目標

  • 建立 graphql 客戶端
  • 封裝請求處理
  • 基於 token 驗證

代碼

步驟

1 安裝依賴包

  • npm install
npm install --save vue-apollo graphql apollo-client apollo-link apollo-link-http apollo-cache-inmemory graphql-tag
  • webpack.json 包清單
"dependencies": {
    "apollo-cache-inmemory": "^1.2.0",
    "apollo-client": "^2.3.0",
    "apollo-link": "^1.2.2",
    "apollo-link-http": "^1.5.4",
    "element-ui": "^2.3.7",
    "graphql": "^0.13.2",
    "graphql-tag": "^2.9.2",
    "vue": "^2.5.2",
    "vue-apollo": "^3.0.0-beta.5",
    "vue-router": "^3.0.1"
  },
若是遇到代碼編譯問題,請對照我使用的包版本

2 編寫 webpack 配置

  • 文件 build/webpack.base.conf.js
module: {
    rules: [
      ...
      {
        test: /\.(graphql|gql)$/,
        exclude: /node_modules/,
        loader: 'graphql-tag/loader'
      }
      ...
    ]
  }
  • import 時,自動作 schema js 包裝
  • import QUERY_USER from '@/graphql/user.graphql' ,把 QUERY_USER 打印看看

3 編寫 config.json 配置

  • 文件 src/utils/config.json
{
  "graphqlServer": "http://127.0.0.1:7001/graphql",
  "tokenName": "UU_AUTH_TOKEN"
}
  • graphqlServer 服務器地址
  • tokenName 本地寫 localStorage key 名稱 ,推薦你們作本地化能夠用 cookie 設置過時時間

4 編寫 VueApollo 適配器

  • 文件 src/apolloProvider.js
import Vue from 'vue'
import {ApolloClient} from 'apollo-client'
import {HttpLink} from 'apollo-link-http'
import {InMemoryCache} from 'apollo-cache-inmemory'
import VueApollo from 'vue-apollo'
import { ApolloLink, concat, split } from 'apollo-link';
import { getMainDefinition } from 'apollo-utilities';
import Config from '@/utils/config.json'

Vue.use(VueApollo)

const httpLink = new HttpLink({
  uri: Config.graphqlServer,
})

const authMiddleware = new ApolloLink((operation, forward) => {
  const token = localStorage.getItem(Config.tokenName) || null
  operation.setContext({
    headers: {
      Authorization: `Bearer ${token}`
    }
  });
  return forward(operation);
})

const apolloClient = new ApolloClient({
  link: concat(authMiddleware, httpLink),
  cache: new InMemoryCache(),
  connectToDevTools: true,
})

const apolloProvider = new VueApollo({
  defaultClient: apolloClient,
  defaultOptions: {
    $loadingKey: 'loading'
  },
  errorHandler (error) {
    console.log('Global apollo error handler')
    console.error(error)
  }
})

export default apolloProvider
authMiddleware 中寫入 Request headers token
apolloClient 中定義 cache: new InMemoryCache()
  • 文件 src/main.js
import apolloProvider from './apolloProvider'
...
new Vue({
  el: '#app',
  provide: apolloProvider.provide(),
  ...
})

5 編寫 schema 定義

  • 項目中的請求定義 我都放在了 目錄 src/graphql 下,有點像 restfulapi 定義
.
├── removeUser.graphql
├── user.graphql
└── users.graphql
  • 我這裏寫了 2 個查詢 1 個更新操做,名稱和服務端定義一致,這樣方便查詢修改

6 編寫 Graphql 請求

  • 文件 src/components/HelloWorld.vue
  • 官方 readme.md 中只寫了一種 組件 方式調用,我仍是喜歡 api 方式
  • 代碼 HelloWorld.vue

6.1 組件 方式

apollo: {
    login: {
      query: gql`
        query queryFun($username: String!, $password: String!) {
          user(username: $username, password: $password) {
            id
            name
            token
          }
        }
      `,
      variables() {
        return {
          username: 'ducafecat',
          password: '345457yftgyhdsfghre'
        }
      },
      manual: true,
      result({data, loading}) {
        console.log(data, loading)
        if (!loading) {
          this.res = data
        }
      }
    }
  }
}
</script>

這種方式作查詢,適合功能單一的展現組件,靈活性感受仍是差了點,有興趣的能夠去掉註釋你們體會下。vue

6.2 api 方式

  • query 查詢
import QUERY_USER from '@/graphql/user.graphql'
...
  methods: {
    handleLogin() {
      this.clearData()
      this.$apollo
        .query({
          // Query
          query: QUERY_USER,
          variables: {
            username: 'ducafecat',
            password: '12321321321321432'
          },
        })
        .then(response => {
          this.loading = false
          this.res = response.data
          localStorage.setItem(Config.tokenName, this.res.user.token)
          alert('登陸成功,寫入Token完成,從新裝載 apolloProvider')
          window.location.reload()
        })
        .catch(error => {
          this.loading = false
          this.err = error
        })
    },
  • mutate 更新
handleRemoveUser() {
      this.clearData()
      this.$apollo
        .mutate({
          // Query
          mutation: MUTATION_REMOVE_USER,
          variables: {
            id: 123
          }
        })
        .then(response => {
          this.loading = false
          this.res = response.data
        })
        .catch(error => {
          this.loading = false
          this.err = error
        })
    },

參考

1 文章

2 組件

相關文章
相關標籤/搜索