graphql(四)

Apollo-GraphQL

Apollo-GraphQL 是基于 GraphQL 封装的一种实现,它可以在服务上进行分层,包括 REST api 和 数据库,它包含客户端和服务端,还有 GUI 开发工具,让开发人员可以快速上手进行开发。

  • 以搭建博客网站为例,有博客列表、分类、博客信息 (query)
  • 点击某个博客,跳转到具体文章内容,返回时有已读标注 (mutation)
  • 服务端使用 apollo-server-express
  • 客户端使用 vue-apollo
  • 数据为静态 json

搭建服务端:

  1. 创建一个文件夹,使用npm init -y 初始化该文件夹
  2. 我们采取 apollo-server-express 来快速搭建服务端
    安装需要的包: npm install apollo-server-express express graphql
  3. 创建入口文件app.js
const express = require("express");
const { ApolloServer } = require("apollo-server-express");
const typeDefs = require("./schema");
const resolvers = require("./resolvers");

const PORT = 4000;
const app = express();
 
const server = new ApolloServer({
  typeDefs,
  resolvers,
  playground: {
    endpoint: `/graphql`,
    settings: {
      "editor.theme": "light"
    }
  }
});
 
server.applyMiddleware({ app });
app.listen(PORT, () =>
  console.log(
    `http://localhost:${PORT}${server.graphqlPath}`
  )
);
  1. 由于是博客的例子,因此这里先把 json格式的数据源造好。我们将数据放入data文件夹下。
[
  {
    "id": 1,
    "title": "js基础部分知识总结",
    "date": "2019-07-11",
    "introduction": "学习来一段时间的js,这篇博客将对vue进行一个小小的总结。",
    "category": "vue",
    "isRead": false
  }
]
[
  {
    "id": 1,
    "html": "<h1>js基础部分知识总结</h1>"
  }
]
  1. 对于apollo-server,最重要的也就是去定义 schema 和 resolvers ,我们要搞清楚如何去定义它们。
  • 定义 schema.js :
    我们要把 schema文件里面的type 转换为 typeDefs要用到的形式, 就需要用到apollo-server-express
const { gql } = require("apollo-server-express");
module.exports = gql `
type Article {
  id: Int!
  title: String!
  date: String!
  introduction: String!
  category: String
  isRead: Boolean!
}
 
type ArticleContent {
  id: Int!
  html: String!
}
 
type Category {
  num: Int!,
  name: String!
}
 
type Query {
  fetchArticles: [Article]
  getAllCategories: [Category]
  getArticleContent(id: Int!): ArticleContent
}
 
type Mutation {
  articleIsRead(id: Int!): Article
}
`
  • 定义 resolvers.js
    resolvers 其实就是 query 和 mutation 的实现过程。也就是说这里会进行数据库的查询或者是 api 的调用等等,最终放回的结果在这里出来。
const articles = require("./data/articles.json");
const articleContent = require("./data/articleContent.json");
 
const resolvers = {
  Query: {
    fetchArticles() {
      return articles;
    },
    getAllCategories() {
      return articles.reduce((pre, cur) => {
        const cate = pre.find(_ => _.name === cur.category);
        if (cate) {
          cate.num++;
        } else {
          const obj = {
            name: cur.category,
            num: 1
          };
          pre.push(obj);
        }
        return pre;
      }, []);
    },
    getArticleContent(_, { id }) {
      return articleContent.find(val => val.id === id);
    }
  },
 
  Mutation: {
    // 标记已读
    articleIsRead(_, { id }) {
      const article = articles.find(val => val.id === id);
      if (!article) {
        throw new Error(`找不到id为 ${id} 的文章`);
      }
      if (article.isRead) {
        return article;
      }
      article.isRead = true;
      return article;
    }
  }
};
module.exports = resolvers;
  1. 现在我们就可以使用 nodemon app.js 来启动服务器查看。

    graphql(四)

搭建客户端:

使用 vue 来搭建项目:

  1. 创建一个文件夹,使用 vue create blog 来创建一个vue的文件。
  2. 使用npm add vue-apollo graphql apollo-boost 安装需要用到的包。
  3. main.js 中配置文件
import Vue from "vue";
import App from './App.vue';
import ApolloClient from "apollo-boost";
import VueApollo from "vue-apollo";
Vue.use(VueApollo);
//创建ApolloClient实例
const apolloClient = new ApolloClient({
  // 在这里需要使用绝对路径
  uri: "http://localhost:4000/graphql"
});
 //保存了可以在接下来被所有子组件使用的 Apollo 客户端实例。
const apolloProvider = new VueApollo({
  defaultClient: apolloClient
}); 
//使用 apolloProvider 选项将它添加到应用程序
new Vue({
  el: "#app",
  apolloProvider,
  render: h => h(App)
});
  1. 我们需要在vue文件的根目录下创建一个vue.config.js文件,用来配置支持 .gql || .graphql 文件后缀的 webpack。这样就可以导入xxx.gql的文件了,不一定非要在.vue文件或者.js文件里面写查询语句。
module.exports = {
  // 支持 gql 文件
  chainWebpack: config => {
    config.module
      .rule("graphql")
      .test(/\.(graphql|gql)$/)
      .use("graphql-tag/loader")
      .loader("graphql-tag/loader")
      .end();
  }
};
  1. 接下来就试一下查询多个数据,比如说一起查询博客文章列表和分类信息。我这里是直接放到app.vue 页面中使用。
import gql from "graphql-tag";
const fetchDataGql = gql`
  {
    fetchArticles {
      id
      title
      date
      introduction
      category
      isRead
    }
    getAllCategories {
      num
      name
    }
  }
`;
 
export default {
  data() {
    return {
      articles: [],
      categories: []
    };
  },
  apollo: {
    fetchData() {
      const vm = this;
      return {
        query: fetchDataGql,
        update(data) {
          vm.articles = data.fetchArticles;
          vm.categories = data.getAllCategories;
        }
      };
    }
  }
};
  1. 打开浏览器控制台查看。确实是只有一次请求就能获取到一个页面上所有的资源!在做项目的时候,遇到一个页面要请求多个REST接口,有些接口经常返回了很多页面上用不到的数据,也就是多余的数据。这样不仅浪费了服务器资源,前后端对接起来也不方便,所以 GraphQL 可以很好地解决这个点。

    graphql(四)

  2. 将获取到的数据渲染到页面中,这里使用的是vuetify,先使用vue add vuetify 安装好vuetify的插件,然后就可以进行使用渲染页面了。


graphql(四)

8. 查询搞定了,下面可以看看变更mutation,当我们点击某篇文章的小圆心的时候,让它改变颜色,并且弹出一个true。我们只需要继续在app.vue中进行定义。

const mArticleISRead = gql`
  mutation articleIsRead($id: Int!) {
    articleIsRead(id: $id) {
      id
      title
      date
      introduction
      category
      isRead
  }
}
`
methods: {
  mutationIsRead(id) {
    this.$apollo.mutate({
      mutation: mArticleISRead,
      //传递参数
      variables: {
        id
      },
      update: (store, { data: { articleIsRead } }) => {
        alert(articleIsRead);
      }
    });
  }
}

当我们点击了小圆心的时候颜色会变成红色,并且会弹出true。

上一篇:UVa 1374 Power Calculus (IDA*或都打表)


下一篇:javascript – GraphQL:更新数组