天天看點

graphql學習(四)

之前的學習,都是使用graphiql或者postman驗證背景api.作為全棧工程師,這不能夠啊,必須要有前端,必須的

今天學習在vue中使用graphql——vue-apollo.

使用apollo有兩個選擇:Apollo Boost或者Apollo Client. 這兩者的差別是:Apollo Boost更傻瓜化,預設幫我們做了很多的配置;而Apollo Client需要自己詳細的配置.對于我這種二把刀的全棧工程師,還是選擇Apollo Boost好了.

安裝node.js什麼的就不贅述了. 先安裝vue cli

1. npm install -g @vue/cli
2. vue create demo           

選擇預設模闆建立項目就可以了.建立成功後,會有如下輸出:

🎉  Successfully created project demo.
👉  Get started with the following commands:

 $ cd demo
 $ npm run serve           

按照提示,我們進入demo,執行npm run serve,成功資訊

DONE  Compiled successfully in 2702ms9:47:28 AM

<s> [webpack.Progress] 100%


  App running at:
  - Local:   http://localhost:8080/
  - Network: http://10.5.92.149:8080/

  Note that the development build is not optimized.
  To create a production build, run npm run build.           

浏覽器裡可以看到:

graphql學習(四)

準備工作就做好了.

接下來需要增加apollo元件. 執行

npm install --save vue-apollo graphql apollo-boost graphql-tag           

在繼續前端之前,我們需要後端做點小修改: 因為前端後端都使用8080端口監聽,是以我們修改一下後端的端口,改成監聽9090.另外,還需要引入新的package "github.com/rs/cors"解決跨域的問題:

// main
func main() {
    h := Register()
    // 解決跨域的問題
    hdl := cors.Default().Handler(h)
    http.Handle("/graphql", hdl)
    fmt.Println("Now server is running on port 9090")
    http.ListenAndServe(":9090", nil)
}           

還有,需要改正一個bug, models/article.go裡的update()函數,要改成如下:

// Update a article
func UpdateArticle(id int, title, content string) (*Article, error) {
    for k, _ := range articleList {
        if articleList[k].ID == id {
            if title != "" {
                articleList[k].Title = title
            }
            if content != "" {
                articleList[k].Content = content
            }
            return &articleList[k], nil
        }
    }
    return nil, errors.New("Article not found")
}           

繼續前端的工作. 建立demo/src/utils目錄,添加檔案apollo.js:

import ApolloClient from 'apollo-boost';
const apolloClient = new ApolloClient({
  // 這裡指向後端的位址、端口和URL
  uri: 'http://127.0.0.1:9090/graphql'
})
export default apolloClient;           

建立/demo/src/graphql目錄,添加檔案article.js:

import gql from 'graphql-tag'
import apolloClient from '../utils/apollo'

// 文章清單
export function getArticles(params) {
 return apolloClient.query({
  query: gql `{
   articles{
    id
    title
    content
   }
  }`,
  variables: params
 })
}

// 單篇文章詳情
export function getArticle(params) {
  return apolloClient.query({
    query: gql `query ($id : Int) {
      article(id: $id) {
        id
        title
        content
      }
    }`,
    variables: params
  })
}

// 添加新文章
export function createArticle(params) {
 return apolloClient.mutate({
  mutation: gql `mutation ($title: String, $content: String) {
   add(title: $title, content: $content){
    id
    title
    content
   }
  }`,
  variables: params
 })
}

// 編輯文章
export function editArticle(params) {
  return apolloClient.mutate({
   mutation: gql `mutation ($id: Int, $title: String, $content: String) {
    update(id: $id, title: $title, content: $content){
     id
     title
     content
    }
   }`,
   variables: params
  })
 }

// 删除文章
export function deleteArticle(params) {
  return apolloClient.mutate({
   mutation: gql `mutation ($id: Int) {
    delete(id: $id){
     id
     title
     content
    }
   }`,
   variables: params
  })
 }           

這裡比較難了解的是傳入參數的解析,對照一下頁面模闆傳入的參數,多比較就能明白了.

然後在demo/components下建立Article.vue檔案,内容如下:

<template>
  <div id="article">
    <div class="list">
      <h1>{{ msg }}</h1>
      <ul>
        <li v-for="(v, k) of list" :key="k">
          文章名稱: {{ v.id }}----------------({{ v.title }})
          <button @click="getArticle(v.id)">詳情</button>
          <button @click="deleteArticle(v.id)">删除</button>
        </li>
      </ul>
    </div>
    <div v-if="article.id > 0">
      <div>文章id:{{ article.id }}</div>
      标題:<input v-model="article.title" type="text"><br>
      文章内容: <textarea v-model="article.content" name="" id="" cols="30" rows="10"></textarea><br>
      <button @click="editArticle">編輯</button><button @click="article={}">取消</button>
    </div>
    <div class="form">
      <h1>添加文章</h1>
      标題:<input v-model="formData.title" type="text"><br>
      文章内容: <textarea v-model="formData.content" name="" id="" cols="30" rows="10"></textarea><br>
      <button @click="createArticle">添加</button>
    </div>
  </div>
  
</template>

<script>
import { getArticles,getArticle,createArticle,deleteArticle,editArticle } from '../graphql/article'

export default {
  name: 'Article',
  props: {
    msg: String
  },
  data() {
    return {
      list: [],
      formData: {
        title: '',
        content: ''
      },
      article: {
        id: 0,
        title: '',
        content: ''
      }
    }
  },
  methods: {
    initData() {
      getArticles()
      .then(res=>{
        console.log('request success')
        this.list = res.data.articles
      })
      .catch(err=>{
        console.log(err)
      })
    },
    getArticle(id) {
      getArticle({id:id})
      .then(res =>{
        this.article = res.data.article
      })
      .catch(err =>{
        console.log(err)
      })
    },
    createArticle() {
      createArticle(this.formData)
      .then(()=>{
        this.initData()
      })
      .catch(err=>{
        console.log(err)
      })
    },
    deleteArticle(id) {
      deleteArticle({id: id})
      .then(() =>{
        this.initData()
      })
      .catch(err=>{
        console.log(err)
      })
    },
    editArticle() {
      editArticle(this.article)
      .then(() =>{
        this.initData()
      })
      .catch(err=>{
        console.log(err)
      })
    }
  },
  mounted() {
    this.initData()
  }
}
</script>

<style>

</style>           

最後修改demo/App.vue:

<template>
  <div id="app">
    <!-- <img alt="Vue logo" src="./assets/logo.png">
    <HelloWorld msg="Welcome to Your Vue.js App"/> -->
    <Article msg="Article List" />
  </div>
</template>

<script>
// import HelloWorld from './components/HelloWorld.vue'
import Article from './components/Article.vue'

export default {
  name: 'App',
  components: {
    // HelloWorld
    Article
  }
}
</script>

<style>
#app {
  font-family: Avenir, Helvetica, Arial, sans-serif;
  -webkit-font-smoothing: antialiased;
  -moz-osx-font-smoothing: grayscale;
  text-align: center;
  color: #2c3e50;
  margin-top: 60px;
}
</style>           

因為頁面比較醜,而且增删改後要重新整理頁面才會顯示新的資料.就不截圖展示了

繼續閱讀