graphql学习(四)

  1. 云栖社区>
  2. 博客>
  3. 正文

graphql学习(四)

eyunshu 2020-02-27 14:23:46 浏览325
展开阅读全文

之前的学习,都是使用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.

浏览器里可以看到:
image
准备工作就做好了.

接下来需要增加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>

因为页面比较丑,而且增删改后要刷新页面才会显示新的数据.就不截图展示了

网友评论

登录后评论
0/500
评论