点击上方“IT平头哥联盟”,选择“置顶或者星标”

与您一起成长~

作者 | 卢铭

基本概念

GraphQL

GraphQL 是一种用于 API 的查询语言,由Facebook开发和开源,是使用基于类型系统来执行查询的服务端运行时(类型系统由你的数据定义)。GraphQL并没有和任何特定数据库或者存储引擎绑定,而是依靠你现有的代码和数据支撑。

背景介绍

相信看了上面的基本概念,大家都是和我一样一脸萌萌哒。所以这里就需要介绍一下其产生的背景和原因。

在我们目前的前后端开发过程中,大部分都是以http请求服务端接口的方式完成交互过程的。在这种场景下,每当需求变化,就需要修改或创建一个新的接口去满足特定的需求。

举个栗子: 在一个商品详情页,当我们需要获取商品详情时,服务端会给前端一个接口,例如:

https://www.example.com/getInfoById?infoId=000000

当前端请求接口时,会返回给一个固定格式的数据,例如:

{data:{title:'商品的标题',content:'商品的描述内容',special:'商品特点',price:'商品价格',image:'商品的图片'}
}

前端接收到数据后,进行各种相应的处理展示,最终将包含有商品标题,商品描述,商品特点,商品价格,商品图片信息的页面展示给用户。

一切看起来都很美好,直到有一天……

产品大摇大摆的走过来,轻描淡写的说道:“能不能把商品的特点去掉,加一个商品的库存,另外还需要再加一个卖家的模块进去。包含卖家的名称和头像,可以点进卖家的详情页,也不用太着急,午饭前上线就行。”

于是前后端坐在一起开始商量,前端弱弱的说:“能不能改一下你的接口,把产品不要的都去掉,产品需要的都加上”。

后端心里说,你当我傻啊,于是一边砸吧嘴一边赶忙说道:“这样改风险太大,好多数据都不在一个表,我不好查。这样,详情页那个接口我就不改了,你不显示不就完了嘛,万一哪天产品那小子的小子再想起来加上,咱俩还得忙活。库存再给你一个接口,卖家信息再给你一个接口,完美,就这么定了。”

前端还想再说什么,可后端的背影已经随着产品越走越远。

就在前端绝望之时,霹雳一声震天响,graphql闪亮登场。

在graphql模式下,假设我们的服务端部分已经部署完成,前端使用vue框架,那么前端部分的请求就可以简化为:

  apollo: {goods: {query() {return gql`{goods(infoId:"${this.infoId}"){titlecontentpriceimage}}`}},store: {query() {return gql`{store(infoId:"${this.infoId}"){store}}`}},seller: {query() {return gql`{seller(infoId:"${this.infoId}"){nameage}}`}}}

可以看到graphql为我们定义了一种类似sql的查询语言,而这种查询语言是用于api的。和之前的数据请求处理不同,在这里,我们只要定义好需要的数据,其他的不再关心,我们就可以按需索取需要的数据。这对于我们的开发提供了更大的自由与便利,只要数据支持,我们就可以摆脱对于服务端接口的依赖,提高生产效率,赢得自由,完成前端的逆袭。

前后端实践

讲完了故事,我们开始讲一些实际的干货。 对于graphql,网上已经有很多实践经验,以下部分是在参考完成实践经验并自我实践后给出的总结归纳。

服务端

服务端的技术选型,我们使用了eggjs框架,配合egg-graphqlegg-graphql插件完成。

1.安装依赖包
$ npm install --save egg-graphql
2.开启插件
// config/plugin.js
exports.graphql = {enable: true,package: 'egg-graphql',
};
//开启 cros 跨域访问
exports.cors = { enable: true, package: 'egg-cors'
}
3.配置graphql路由和跨域
//config/config.default.js
// graphql路由
config.graphql = {
router: '/graphql',
// 是否加载到 app 上,默认开启
app: true,
// 是否加载到 agent 上,默认关闭
agent: false,
// 是否加载开发者工具 graphiql, 默认开启。路由同 router 字段。使用浏览器打开该可见。
graphiql: true,
// graphQL 路由前的拦截器
onPreGraphQL: function*(ctx) {},
// 开发工具 graphiQL 路由前的拦截器,建议用于做权限操作(如只提供开发者使用)
onPreGraphiQL: function*(ctx) {},
}
// cors跨域
config.cors = {    origin: '*',    allowMethods: 'GET,HEAD,PUT,POST,DELETE,PATCH,OPTIONS'
}
4.开启graphql中间件
//config/config.default.js
exports.middleware = [ 'graphql' ];

项目配置告于段落。

5.编写业务员代码

下面开始写代码。 目录结构如下:

├── app
│   ├── graphql                          //graphql 代码,所有和graphql相关的代码都在这里,已经在前面做好了配置
│   │    └── common                   //通用类型定义,graphql有一套自己的系统类型,除此之外还可以自定义
│   │     |    |── scalars                 //自定义类型定义
│   │     |    |  └──  date.js            // 日期类型实现
│   │     |   └── resolver.js            //合并所有全局类型定义
│   │     |   └── schema.graphql     // schema 定义
│   │    └──goods                       // 商品详情的graphql模型
│   │         └── connector.js         //连接数据服务
│   │         └── resolver.js            //类型实现,和goods中schema.graphql定义的模型相对应,是其具体的实现
│   │         └── schema.graphql    //schema 定义,在这里定义商品详情数据对象
│   │    └──store                        // 库存的graphql模型
│   │         └── connector.js         //连接数据服务
│   │         └── resolver.js            //类型实现
│   │         └── schema.graphql    //schema 定义,在这里定义商品详情数据对象
│   │    └──seller                       // 卖家信息的graphql模型
│   │         └── connector.js         //连接数据服务
│   │         └── resolver.js            //类型实现
│   │         └── schema.graphql    //schema 定义,在这里定义商品详情数据对象
│   │    └──query                       // 所有的查询都会经过这里,这里是一个总的入口
│   │         └── schema.graphql    //schema 定义
│   ├── service
│   │   └── goods.js                    //商品详情的具体实现
│   │   └── store.js                     //库存的具体业务逻辑
│   │   └── seller.js                     //卖家信息的具体业务逻辑
│   └── router.js

app/graphql/query/schema.graphql是整个graphql查询的总入口,所有需要查询的对象都要在这里定义。它的定义形式如下:

#定义查询对象,在graphql里的注释使用#号
type Query {
goods(#查询条件,相当于接口的入参商品idinfoId: ID!
):Goods #Goods是在app/graphql/goods/schema.graphql中定义的商品详情
}

在总入口中涉及到的查询对象,都需要在graphql文件夹下建立相应的文件夹,如上文中提到的goods,就在app/graphql文件夹中存在相应的goods文件夹。goods文件夹包含三个部分:schema.graphql,resolve.js和connector.js。 schema.graphql中需要定义总入口中提及的Goods对象:

# 商品
type Goods {# 流水号infoId: ID!# 商品标题title:String!,# 商品内容content:'String!,#商品特点special:'String!,#商品价格price:'nt!,#商品图片image:'String!,
}

graphql自带一组默认标量类型,包括Int,Float,String,Boolean,ID。在定义字段时需要注明类型,这也是graphql的特点之一,是支持强类型的。如果非空,就在类型后面跟上一个!号。graphql还包括枚举类型,列表和自定义类型,具体可以查看相关文档。

resolve.js是数据类型的具体实现,依赖connector.js完成:

'use strict'
module.exports = {Query: {goods(root, {infoId}, ctx) {return ctx.connector.goods.fetchById(infoId)}
}

connector.js是连接数据的具体实现,可以使用dataloader来降低数据访问频次,提高性能:

'use strict'
//引入dataloader,是由facebook推出,能大幅降低数据库的访问频次,经常在Graphql场景中使用
const DataLoader = require('dataloader')
class GoodsConnector {constructor(ctx) {  this.ctx = ctx  this.loader = new DataLoader(id=>this.fetch(id))}fetch(id) {  const goods = this.ctx.service.goods  return new Promise(function(resolve, reject) {    const goodsInfo = goods.getInfoById(id)    resolve([goodsInfo])  //注意这里需要返回数组形式})}fetchById(id) {  return this.loader.load(id)}
}
module.exports = GoodsConnector

上面代码中涉及的this.ctx.service.goods就是app/service文件夹下的goods.js文件导出的方法对象,也就是获取数据的具体业务逻辑:

const Service = require('egg').Service
const {createAPI} = require('../util/request')//实现的http请求
class GoodsService extends Service {
// 获取商品详情async getInfoById(infoId) {const result = await createAPI(this, 'example/getInfoById', 'get', {infoId})return result}
}
module.exports = GoodsService

获取数据可以用你能实现的任何方式,可以直接从数据库获取,也可以用http从现有的接口获取。 这样一个使用egg框架实现的graphql服务就完成了。 下面说一下前端。

前端

我们会使用vue配合Apollo完成前端搭建。

1 安装依赖包
npm install --save vue-apollo apollo-client
2.引用apollo
import { ApolloClient } from 'apollo-client'
import { HttpLink } from 'apollo-link-http'
import { InMemoryCache } from 'apollo-cache-inmemory'
import VueApollo from 'vue-apollo'
3.配置链接
const httpLink = new HttpLink({// 需要配置一个绝对路径uri: 'http://exzample.com/graphql',
})
4.创建ApolloClient实例和PROVIDER
// Create the apollo client
const apolloClient = new ApolloClient({link: httpLink,cache: new InMemoryCache(),connectToDevTools: true,
})
const apolloProvider = new VueApollo({defaultClient: apolloClient,
})
4.在vue中引入使用
Vue.use(VueApollo);
5.根实例引用
    var vm = new Vue({el: '#app',provide: apolloProvider.provide(),router,components: {app: App},render: createEle => createEle('app')})
6.使用
<script>
import gql from "graphql-tag";
export default { data() { return {goods:{},infoId:123123}; }, apollo: { goods: {query() {return gql`{goods(infoId:"${this.infoId}"){titlecontentpriceimage}}`}},}}; </script>

展望

graphql对于目前接口数量多,难维护,扩展成本高,数据格式不可预知,文档难维护等问题给出了一个相对完善的方案,相信在未来,它将是我们工作中不可或缺的一部分。

- end -

用心分享 一起成长 做有温度的攻城狮

每天记得对自己说:你是最棒的!

往期热点

关于重绘和回流,你真的了解了?

面试官问:能否模拟实现JS的new操作符
2018文章汇总

看完这几道 Promise 面试题,还被面试官问倒就活该~

!福利,2019免费送书 5折掘金小册码

GraphQL-前端进阶的利剑与桥梁相关推荐

  1. IT:前端进阶技术路线图(初级→中级→高级)之初级(研发工具/HTML/CSS/JS/浏览器)/中级(研发链路/工程化/库/框架/性能优化/工作原理)/高级(搭建/中后台/体验管理等)之详细攻略

    IT:前端进阶技术路线图(初级→中级→高级)之初级(研发工具/HTML/CSS/JS/浏览器)/中级(研发链路/工程化/库/框架/性能优化/工作原理)/高级(搭建/Node/IDE/中后台/体验管理/ ...

  2. 前端进阶之路: 前端架构设计(2)-流程核心

    可能很多人和我一样, 首次听到"前端架构"这个词, 第一反应是: "前端还有架构这一说呢?" 在后端开发领域, 系统规划和可扩展性非常关键, 因此架构师备受重视 ...

  3. 前端进阶必备技能:Vue中如何定制动画效果

    作为前端程序员,前端火起来的短短几年里技术更新迭代特别快,不仅是新的框架繁多,Vue,React,Angular轮番上场,各种工具,插件,库也是琳琅满目,就连基础的JavaScript语法的更新也是年 ...

  4. 自学前端开发,前端进阶阶段需要学习哪些知识?

    今天要跟大家分享的文章是关于web前端进阶阶段需要学习哪些知识?已经入门web前端想要提升自己技术的小伙伴们来和小编一起看一看本篇文章吧,希望本篇文章能够对大家有所帮助. 1.完善我们的基础知识 (1 ...

  5. fifo算法_前端进阶算法6:一看就懂的队列及配套算法题

    引言 队列这种数据结构,据瓶子君了解,前端需要了解的队列结构主要有:双端队列.滑动窗口,它们都是算法中是比较常用的数据结构. 因此,本节主要内容为: 数据结构:队列(Queue) 双端队列(Deque ...

  6. web前端进阶<7>:3d图像翻转效果

    这几天又闲着无聊,自己学的东西又有一点不牢固了,需要写一个小程序来巩固一下,相信大家也时有同感吧!那么这一期就给大家带来一个炫酷的相册3d翻转效果,如果搭上炫酷的文字和动画那肯定是高端.霸气上档次的: ...

  7. 前端进阶垫脚石-前端工程化

    什么是前端工程化 前端工程化,就是降本提效的体现 广义上,前端工程化包含一切以降低成本.提高效率.保障质量为目的的手段 通过一系列的规范.流程.工具达到研发提效.自动化.保障质量.服务稳定.预警监控等 ...

  8. 大前端进阶!NodeJS、Npm、Es6、Babel、Webpack、模块化开发

    文章目录 大前端进阶 一.Node.js 1.1.Nodejs介绍和安装 1.2 .Nodejs入门 1.2.1.快速入门-Hello World 1.2.2.Node - 实现请求响应 1.2.3. ...

  9. LEARN_前端进阶_深浅拷贝原理

    详细解析赋值.浅拷贝和深拷贝的区别 赋值(Copy) 赋值是将某一数组或对象赋给某个变量的过程,分类下面 2 部分: 基本数据类型:赋值,赋值之后两个变量互不影响 引用数据类型:赋址,两个变量具有相同 ...

最新文章

  1. python中构造方法可以被继承吗_构造函数是在python中继承的吗
  2. python属性错误怎么改_属性错误:无法设置属性
  3. 0114练习 彩票、验证码、双色球的随机数 输出
  4. cocos2d 屏幕適配_Cocos2d-x 3.1 一步步做屏幕适配
  5. Python随手记—各种方法的使用
  6. zoj 2110 dfs,剪枝
  7. 机器学习与计算机视觉(绘图)
  8. 使用grunt合并压缩js、css文件
  9. 这个严重 0day 可导致79款 Netgear 路由器遭远程接管,无补丁
  10. 第十二章——SQLServer统计信息(3)——发现过期统计信息并处理
  11. 1.软件工程--软件建模与文档协作 --- 软件开发过程
  12. android qq卡屏代码,2015最新卡死安卓QQ代码 卡到让对方手机QQ无响应及代码分享...
  13. Exosip源码学习
  14. Android6.0动态壁纸,全局透明动态壁纸
  15. 图·思·腾·想·宗教符号
  16. 亚马逊资源名称 (ARN) 和 AWS 服务命名空间
  17. 精密仪器及机械类毕业论文文献包含哪些?
  18. python爬虫入门教程04:招聘信息爬取
  19. matlab julia分形图,Three.js 朱丽亚集(Julia set)分形图案
  20. 斐波那契数列(C/C++)

热门文章

  1. 人工智能 选择填空题
  2. Python相关工具使用01_设置双击直接打开.ipynb文件
  3. python基于php+MySQL的网络精品课程教学平台
  4. 印象大使_宣布开放组织大使计划
  5. 比穷更可怕的,是你不会用!ZBrush小技巧和常用快捷键
  6. PowerMockito when 不生效原因
  7. 自定义kafka高效的protoStuff序列化
  8. C++解题报告——Kas(动态规划)
  9. 小白也能操作的交换机基本配置,努力加油奋斗!
  10. 【转载】瑞萨CS+ for CC对单片机IO口进行位操作