Graphql实践——像axios一样使用Graphql
Graphql尝鲜
在只学习graphql client端知识的过程中,我们常常需要一个graphql ide来提示graphql语法,以及实现graphql的server端来进行练手。
graphql社区提供了graphiql让我们使用
graphiql (npm):一个交互式的运行于浏览器中的 GraphQL IDE.
但graphiql提供的live demo基本打不开,难道刚接触graphql就要自己实现graphql的server端?
好在github用graphql写了一套api,我们可以去这里,登陆后即可体验一把graphql。
关于graphql的基础知识可以去这里看看
graphql client端选择
graphql在前端实现有以下方案。
Relay (github) (npm):Facebook 的框架,用于构建与 GraphQL 后端交流的 React 应用。
Apollo Client (github):一个强大的 JavaScript GraphQL 客户端,设计用于与 React、React Native、Angular 2 或者原生 JavaScript 一同工作。
graphql-request:一个简单的弹性的 JavaScript GraphQL 客户端,可以运行于所有的 JavaScript 环境(浏览器,Node.js 和 React Native)—— 基本上是 fetch 的轻度封装。
Lokka:一个简单的 JavaScript GraphQL 客户端,可以运行于所有的 JavaScript 环境 —— 浏览器,Node.js 和 React Native。
nanogql:一个使用模板字符串的小型 GraphQL 客户端库。
从npm download数量上看Apollo Client是最多的,并且Apollo也有服务端的解决方案,所以这里选择Apollo Client作为graphql的client端
apollo client对于web 框架都有具体的实现,但是我更希望能像axios那样去使用graphql,而不是每套web框架都要去学一下具体实现,那样会折腾死自己。
初始化项目
// 使用vue-cli初始化项目 vue init webpack-simple my-project npm i
安装graphql
npm i apollo-cache-inmemory apollo-client apollo-link apollo-link-http npm i graphql graphql-tag
项目结构如下
. ├── index.html ├── package.json ├── package-lock.json ├── README.md ├── src │ ├── App.vue │ ├── graphql // 接口 │ │ ├── search.graphql │ │ └── index.js // export所有接口 │ ├── main.js │ └── utils │ └── graphql.js // 对Apollo-client封装 └── webpack.config.js
apollo-client
接下来对apollo-client进行封装,加上中间件(实现类似于axios拦截器的效果)。
graphql.js
import ApolloClient from 'apollo-client' import { InMemoryCache } from 'apollo-cache-inmemory' import { HttpLink } from 'apollo-link-http' import { onError } from 'apollo-link-error' import { ApolloLink, from } from 'apollo-link' const token = '598ffa46592d1c7f57ccf8173e47290c6db0d549' const Middleware = new ApolloLink((operation, forward) => { // request时对请求进行处理 console.log('Middleware', operation, forward) }) const Afterware = new ApolloLink((operation, forward) => { return forward(operation).map(response => { // 服务器返回数据 console.log('Afterware--response', response) return response }) }) const errorLink = onError(({ graphQLErrors, networkError }) => { if (graphQLErrors) graphQLErrors.map(({ message, locations, path }) => console.log( `[GraphQL error]: Message: ${message}, Location: ${locations}, Path: ${path}`, ), ); if (networkError) console.log(`[Network error]: ${networkError}`); }); const httpLink = new HttpLink({ uri: 'https://api.github.com/graphql', // 配置请求url headers: { // 配置header Authorization: `Bearer ${token}` } }) const cache = new InMemoryCache() // 缓存 export default new ApolloClient({ link: from([Middleware, Afterware, errorLink, httpLink]), cache })
配置webpack支持.graphql文件
// 在rules下添加以下规则 { test: /\.(graphql|gql)$/, exclude: /node_modules/, loader: 'graphql-tag/loader', }
search.graphql
query searchR ($keyword: String!) { search (query: $keyword , type: REPOSITORY){ userCount } }
在/graphql/index.js export所有接口
import client from '../utils/graphql' // import gql from 'graphql-tag' import * as searchGql from './search.graphql' /** searchGql模块 **/ export const search = (params) => client.query({query: searchGql.search, variables: params})
到这里我们已经可以直接调用/graphql/下导出的function
使用github接口实现一个简单的搜索功能
具体实现就不贴出来了,全部代码已经放到github,欢迎star。
run的时候有记得把token换成自己的,因为我的token有可能已经失效。
Graphql分页
graphql实现分页有以下两种方式:
- 基于偏移量,需要提供第几页, 每页的数量
- 基于游标或者id,提供每页数量,与 游标/id。
对于游标分页Relay(Facebook家的Graphql库) 定了一套规范 Relay-style cursor pagination
基于偏移量的分页实现简单,但存在以下问题:
性能问题,虽然可以使用 “延迟关联” 解决,但会使sql语句变得复杂
# 假设 有一个 product商品表,当商品表数量足够多时,这个查询会变得非常缓慢, SELECT id, name FROM product LIMIT 1000, 20; # 如果我们提供一个边界值,比如id,无论翻页到多么后面,其性能都会很好 SELECT id, name FROM product WHERE id > 1000 LIMIT 20;
删除列表数据时,导致获取下一页的数据缺失
# 假设 总共有11条数据,一页显示10条,总页数为 2 页。 # 当调用接口删除 第 1 页的 1 条数据,然后进行翻页时,因为只剩下10条数据,所以下面的sql会查不到数据。 SELECT id, name FROM product LIMIT 10, 10;
基于游标/ID 的分页,也存在硬伤:
- 如何实现跳往第 n 页的功能
难道要获取 相应的游标再进行翻页? 所以它更适用于无限加载,或者只有 上一页/下一页 的情景上,对于跳往第n页还是需要用到基于偏移量的分页。
所以我们需要同时支持这两种分页。
Relay 式的游标分页
Relay 定义了 PageInfo
,Edges
,Edge Types
,Node
,Cursor
等对象 用于实现灵活的分页。