欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  IT编程

前端从零开始学习Graphql

程序员文章站 2023-11-04 13:15:10
学习本姿势需要电脑装有node,vue-cli相关环境,以及要有node,express,koa,vue相关基础 本文相关demo的github地址: 一 Graphql概述 它是什么?从哪里来?要干什么? 简单地讲,对于前端,它就是让你舒舒服服发请求的 严格的说,它是一种api设计思想,用来取代r ......

学习本姿势需要电脑装有node,vue-cli相关环境,以及要有node,express,koa,vue相关基础

本文相关demo的github地址:

node服务:https://github.com/liuming888/graphql_node_demo.git
vue项目:https://github.com/liuming888/graphql_vue_demo.git
					

一 graphql概述

它是什么?从哪里来?要干什么?

简单地讲,对于前端,它就是让你舒舒服服发请求的

严格的说,它是一种api设计思想,用来取代restful api的一种前端处于主导地位的api规范。它把前端所需要的api用类似图数据结构(graph)的方式展现出来,让前端很方便的获取所需要的数据。

 

特点

需要什么就获取什么数据

支持关系数据的查询

api无需定义各种路由,完全数据驱动

无需管理api版本,一个版本持续演进

支持大部分主流开发语言和平台

强大的配套开发工具

 

起源,restful api的问题

如果采用restful api的话,后端要持续维护api doc,但是实际场景是这样的:

1.来了新需求,后端先评估和开发,后端弄得差不多,前端才开始,然后中间后端一顿猛改

2.由于多种原因,后端经常自己偷偷改掉传参或者返回值,完了接口报错,测试姐姐把前端叫过去一顿批,前端一脸懵圈,仔细检查,发现问题,找后端撕x,这样一个循环非常影响开发效率。

gql出现

由于上面这一堆的问题,facebook公司2012年内部实践了graphql,15年刚开源的时候引起了很多大公司和社区关注,落地了很多规范和框架。需要了解详细历史可以看看底下的youtube视频。

这种叫graphql的东西帮助人们经过一系列定义和规范,可以发送gql请求非常方便的拿到想要的数据,甚至还可以修改数据,而不用后台的配合,而且一旦schema确定(数据库那边定义好),前后端就可以快速并行开发,例如下图获得某个用户的信息,我想要这个用户的什么属性就写什么,graphiql工具可以进行完整的详细的提示,请求主体简单明了

    query{
          student{
            id
            name
            age
          }
          course{
            id
            title
          }
        }

gql理想使用场景

数据库建好模型,前后端可以同步开始开发需求,前端只有少数需要依赖后端接口,前端开发过程中可以方便的拿到任何想要的数据,从而节省大量联调接口的时间,迅速的完成一个项目。

实现原理

gql的实现挺复杂的,代码有点难懂,不过原理说起来比较简单易懂

var { graphql, buildschema } = require('graphql');

 

// construct a schema, using graphql schema language
var schema = buildschema(`
type query {
    hello: string
  }
`);

 

// the root provides a resolver functionfor each api endpoint
var root = {
  hello: () => {
return'hello world!';
  },
};

 

// run the graphql query '{ hello }' and print out the response
graphql(schema, '{ hello }', root).then((response) => {
  console.log(response);
});

如上就是一个最简单的node版的gql服务器 gql把定义好的schema拿到,用root之类的resolve解析器去解析发送来的'{ hello }'请求,然后返回给相应的json值。上面代码打印出的response如下

{ 
    data: 
        { 
            hello: 'hello world!'
        } 
}

当然,resolve逐层解析会有一些问题,如果resolve请求数据库,就要用到dataloader

dataloader是gql服务器性能的关键一环,也是gql社区的主要推动完善方向,就像react里面的shouldcomponentupdate一样制约着gql服务器的性能。

dataloader 能让我们从数据库读取数据并让数据能被 graphql 处理,我们使用 dataloader,而不是直接通过 sql 查询从数据库获取数据,将 dataloader 作为代理以减少我们实际需要发送给数据库的 sql 查询。 dataloader 使用批处理和缓存的组合来实现。如果同一个客户端请求会造成多次请求数据库,dataloader 会整合这些问题并从数据库批量拉取请求数据。dataloader 会同时缓存这些数据,当有后续请求需要同样资源时可以直接从缓存获取到。

具体使用

通过服务端对请求的元数据的type进行严格的定义,我们只要在客户端发送gql请求就能返回期望的相应类型的数据

下图是请求格式,query【请求】,mutation【修改】和subscribe【订阅】是三种api发送方式,query用的多一些,mutation相对传统的restful来说不够可靠和安全,subscribe类似websocket

query 
{
  schema {
    types {
      id
      name // 获取根字段名
      fields {
        id
        name // 获取字段名
      }
    }
  }
}
			

和restful比较的优缺点

优点

优点就是后端可以少招几个写接口的

前后端一起开发,节约工期

较少维护api文档,节省精力

说了这些,其实单对于前端来说,帮助不算特别大

缺点和难推广的地方

后端或者中间层把gql封装相应业务对接数据库是难点,需要高端人力

需要前端多少学一点类sql语句,不过大部分场景可以封装好固定的sql语句

封装gql不好会产生sql性能问题,三级嵌套联查还有n+1的老问题又会冒出来,需要持续优化

前端排除bug需要一定的后端知识,前后端架构多少了解一些

..

 

二 hello word

简易版的hello world

npm install graphql

 

然后使用 node hello.js 以运行 hello.js 中的代码:

 

var { graphql, buildschema } = require('graphql');

 

var schema = buildschema(`

type query {

hello: string

}

`);

 

var root = { hello: () => 'hello world!' };

 

graphql(schema, '{ hello }', root).then((response) => {

console.log(response);

});

 

控制台打印出hello world!

 

express版hello world

npm install express express-graphql graphql

 

然后使用 node server.js 以运行 server.js 中的代码:

 

var express = require('express');

var graphqlhttp = require('express-graphql');

var { buildschema } = require('graphql');

 

var schema = buildschema(`

type query {

hello: string

}

`);

 

var root = { hello: () => 'hello world!' };

 

var app = express();

app.use('/graphql', graphqlhttp({

schema: schema,

rootvalue: root,

graphiql: true, // 是否开启调试模式(生产环境注意得关掉)

}));

app.listen(4000, () => console.log('now browse to localhost:4000/graphql'));

 

访问

前端从零开始学习Graphql

 

 

koa版hello world

npm install koa graphql koa-graphql koa-mount

 

 

然后使用 node server.js 以运行 server.js 中的代码:

const koa = require('koa');

const mount = require('koa-mount');

const { buildschema } = require('graphql');

const graphqlhttp = require('koa-graphql');

const app = new koa();

 

const schema = buildschema(`

type query {

hello: string

}

`);

 

const root = {

hello: () => 'hello world!',

};

 

app.use(

mount(

'/graphql',

graphqlhttp({

schema: schema,

rootvalue: root,

graphiql: true,

})

)

);

 

app.use(async ctx => {

ctx.body = 'hello world';

});

 

app.listen(3000);

 

访问

前端从零开始学习Graphql

 

 

三 进阶

参考资料:用node创建graphql api:代码量少,性能高

 

基于express

代码看 node-graphql-demo项目 前端从零开始学习Graphql 然后yarn npm run dev

github地址: https://github.com/liuming888/graphql_node_demo.git master分支

创建 project

我们现在来创建我们的 project。打开一个新的 terminal,执行以下命令,即可使用缺省值创建 package.json 文件:

mkdir node-graphql-demo

cd node-graphql-demo

npm init -y

 

接下来,我们需要安装以下依赖:

npm install graphql express express-graphql sqlite3 --save

这样就会安装 express 框架,node.js 上的 graphql 实现,express 和 sqlite3 的 graphql 中间件。为简单起见,我们使用 sqlite3 作为数据库。

 

graphql 是一个支持库,并且在我们这里是一个必要的模块

 

express 是一个简洁而灵活的 node.js web应用框架, 提供了一系列强大特性帮助你创建各种 web 应用,和丰富的 http 工具。

 

express-graphql graphql http服务器中间件

 

sqlite是一个进程内的库,实现了自给自足的、无服务器的、零配置的、事务性的 sql 数据库引擎。它是一个零配置的数据库,这意味着与其他数据库一样,您不需要在系统中配置。就像其他数据库,sqlite 引擎不是一个独立的进程,可以按应用程序需求进行静态或动态连接。sqlite 直接访问其存储文件

 

创建 graphql 服务器

创建工程并引入基本依赖包之后,现在来创建 api 服务器。在工程文件夹里,创建 index.js 文件,并引入下列内容:

const express = require('express');

const sqlite3 = require('sqlite3').verbose();

const graphql = require("graphql");

const expressgraphql = require("express-graphql");

上面的代码的目的是:为express导入express,sqlite3,graphql和graphql中间件。

 

接下来,添加下列代码,在当前文件夹中创建一个 express 应用程序和名为 my.db 的 sqlite 3 数据库:

const app = express();

const database = new sqlite3.database("./my.db");

 

然后,添加 createcontacttable() 方法,在数据库中创建 contacts 表并马上调用函数:

const createcontacttable = () => {

const query = `

create table if not exists contacts (

id integer primary key,

firstname text,

lastname text,

email text unique)`;

return database.run(query);

}

createcontacttable();

我们创建了一个 sql 表来存储 contacts的基本信息。每个 contact 的基本信息包括:唯一的标识、名、姓和 email。

 

接下来,添加下列代码来定义一个 graphql 类型:

const contacttype = new graphql.graphqlobjecttype({

name: "contact",

fields: {

id: { type: graphql.graphqlid },

firstname: { type: graphql.graphqlstring },

lastname: { type: graphql.graphqlstring },

email: { type: graphql.graphqlstring }

}

});

我们使用基本的内置 graphql 类型,如 graphqlid 和 graphqlstring 来创建我们自定义类型,对应数据库中的 contact。

相关链接:

graphqlid: https://graphql.github.io/graphql-spec/draft/#sec-id

graphqlstring: https://graphql.github.io/graphql-spec/draft/#sec-string

 

接着,定义查询类型,如下所示:

var querytype = new graphql.graphqlobjecttype({

name: 'query',

fields: {

contacts: {

type: graphql.graphqllist(contacttype),

resolve: (root, args, context, info) => {

return new promise((resolve, reject) => {

database.all("select * from contacts;", function (err, rows) {

if (err) {

reject([]);

}

resolve(rows);

});

});

 

}

},

contact: {

type: contacttype,

args: {

id: {

type: new graphql.graphqlnonnull(graphql.graphqlid)

}

},

resolve: (root, {

id

}, context, info) => {

return new promise((resolve, reject) => {

 

database.all("select * from contacts where id = (?);", [id], function (err, rows) {

if (err) {

reject(null);

}

resolve(rows[0]);

});

});

}

}

}

});

我们的查询有两个字段: contacts,可以用来获取数据库中的所有 contacts,而 contact 则根据 id 获取一个 contact 信息。 contact 字段允许所需的 id 参数为 graphqlid 类型。

每个字段都有一个 type,用来说明返回数据的类型,args 定义期望从客户端得到的参数, resolve 则定义了在获取数据逻辑中实际使用的方法。

对于前两个字段, resolve() 方法是实际逻辑发生的地方—— 我们简单调用 database.all() 和 database.run() 方法来执行正确的 sql 查询,以便从 sqlite 获取数据,返回一个 promise 来处理得到的数据。

我们可以从resolve()方法的第二个参数访问任何传递的参数。

接下来,我们创建一个 mutation 类型,用于创建、更新和删除操作: https://graphql.github.io/graphql-spec/draft/#sec-mutation

var mutationtype = new graphql.graphqlobjecttype({

name: 'mutation',

fields: {

createcontact: {

type: contacttype,

args: {

firstname: {

type: new graphql.graphqlnonnull(graphql.graphqlstring)

},

lastname: {

type: new graphql.graphqlnonnull(graphql.graphqlstring)

},

email: {

type: new graphql.graphqlnonnull(graphql.graphqlstring)

}

},

resolve: (root, {

firstname,

lastname,

email

}) => {

return new promise((resolve, reject) => {

database.run('insert into contacts (firstname, lastname, email) values (?,?,?);', [firstname, lastname, email], (err) => {

if (err) {

reject(null);

}

database.get("select last_insert_rowid() as id", (err, row) => {

 

resolve({

id: row["id"],

firstname: firstname,

lastname: lastname,

email: email

});

});

});

})

 

}

},

updatecontact: {

type: graphql.graphqlstring,

args: {

id: {

type: new graphql.graphqlnonnull(graphql.graphqlid)

},

firstname: {

type: new graphql.graphqlnonnull(graphql.graphqlstring)

},

lastname: {

type: new graphql.graphqlnonnull(graphql.graphqlstring)

},

email: {

type: new graphql.graphqlnonnull(graphql.graphqlstring)

}

},

resolve: (root, {

id,

firstname,

lastname,

email

}) => {

return new promise((resolve, reject) => {

database.run('update contacts set firstname = (?), lastname = (?), email = (?) where id = (?);', [firstname, lastname, email, id], (err) => {

if (err) {

reject(err);

}

resolve(`contact #${id} updated`);

 

});

})

}

},

deletecontact: {

type: graphql.graphqlstring,

args: {

id: {

type: new graphql.graphqlnonnull(graphql.graphqlid)

}

},

resolve: (root, {

id

}) => {

return new promise((resolve, reject) => {

database.run('delete from contacts where id =(?);', [id], (err) => {

if (err) {

reject(err);

}

resolve(`contact #${id} deleted`);

 

});

})

 

}

}

}

});

我们的 mutation 类型有三个字段:

createcontact 创建 contacts;

updatecontact 更新 contacts;

deletecontact 删除 contacts.

所有的字段都接受符合 args 属性定义的参数,并由一个 resolve() 方法来获取传递过来的参数,执行相应的 sql 操作,并返回一个 promise。

然后,创建 graphql schema: https://graphql.github.io/graphql-spec/draft/#sec-schema

const schema = new graphql.graphqlschema({

query: querytype,

mutation: mutationtype

});

graphql schema 是 graphql 的核心概念,它定义了连接到服务器的客户端可用的功能。我们传递已定义的 query 和 mutation 类型到 schema。

最后,挂载到 /graphql 端点,在 4000 端口运行 express 服务器:

app.use("/graphql", expressgraphql({ schema: schema, graphiql: true}));

app.listen(4000, () => {

console.log("graphql server running at http://localhost:4000.");

});

保存 index.js 文件,返回到你的 terminal。运行下列命令,启动服务器:

node index.js

 

 

怎样使用 graphql api

构建客户端前,可以使用 graphql 接口来测试你的 api。

访问网址 : http://localhost:4000/graphql ,并运行下列 mutation query:

mutation {

createcontact(firstname: "jon", lastname: "snow", email: "jonsnow@thenightswatch.com") {

id,

firstname,

lastname,

email

}

}

 

使用下列 mutation,可以更新 id 为 1 的 contact:

mutation {

updatecontact(id: 1, firstname: "aegon", lastname: "targaryen", email: "aegontargaryen@ironthrone.com")

}

 

也可以使用下列 mutation 来删除 id 为 1 的 contact:

mutation {

deletecontact(id: 1)

}

 

最后,使用如下 query,可以获取数据库中所有 contacts 信息:

query {

contacts {

id

firstname

lastname

email

}

}

 

使用下面 query,可以获取一个 contact 的信息:

query {

contact(id: 1) {

id

firstname

lastname

email

}

}

 

这里,我们得到的是 id 为 1 的 contact 信息。

 

 

基于koa (和express类似)

 

代码看 koa-demo项目 前端从零开始学习Graphql 然后yarn npm run dev

github地址: https://github.com/liuming888/graphql_node_demo.git koa分支

 

 

创建 project

我们现在来创建我们的 project。打开一个新的 terminal,执行以下命令,即可使用缺省值创建 package.json 文件:

mkdir koa-demo

cd koa-demo

npm init -y

 

接下来,我们需要安装以下依赖:

npm install graphql koa koa-graphql koa-mount sqlite3 --save

这样就会安装 koa库,node.js 上的 graphql 实现,koa 和 sqlite3 的 graphql 中间件。为简单起见,我们使用 sqlite3 作为数据库。

 

graphql 是一个支持库,并且在我们这里是一个必要的模块

 

koa 是一个新的 web 框架,由 express 幕后的原班人马打造, 致力于成为 web 应用和 api 开发领域中的一个更小、更富有表现力、更健壮的基石。 通过利用 async 函数,koa 帮你丢弃回调函数,并有力地增强错误处理。 koa 并没有捆绑任何中间件, 而是提供了一套优雅的方法,帮助您快速而愉快地编写服务端应用程序

 

koa-graphql graphql http服务器中间件

 

koa-mount 让多个koa.js子应用合并成一个父应用,用请求的前缀区分子应用

 

sqlite是一个进程内的库,实现了自给自足的、无服务器的、零配置的、事务性的 sql 数据库引擎。它是一个零配置的数据库,这意味着与其他数据库一样,您不需要在系统中配置。就像其他数据库,sqlite 引擎不是一个独立的进程,可以按应用程序需求进行静态或动态连接。sqlite 直接访问其存储文件

 

创建 graphql 服务器

创建工程并引入基本依赖包之后,现在来创建 api 服务器。在工程文件夹里,创建 index.js 文件,并引入下列内容:

const koa = require('koa');

const mount = require('koa-mount');

const graphql = require('graphql');

const graphqlhttp = require('koa-graphql');

const sqlite3 = require('sqlite3').verbose();

const app = new koa();

 

const database = new sqlite3.database('./my.db');

const createcontacttable = () => {

const query = `

create table if not exists contacts (

id integer primary key,

firstname text,

lastname text,

email text unique)`;

return database.run(query);

};

// 创建了一个 sql 表来存储 contacts的基本信息。每个 contact 的基本信息包括:唯一的标识、名、姓和 email。

createcontacttable();

 

// 定义一个 graphql 类型

// 使用基本的内置 graphql 类型,如 graphqlid 和 graphqlstring 来创建我们自定义类型,对应数据库中的 contact。

const contacttype = new graphql.graphqlobjecttype({

name: 'contact',

fields: {

id: { type: graphql.graphqlid },

firstname: { type: graphql.graphqlstring },

lastname: { type: graphql.graphqlstring },

email: { type: graphql.graphqlstring },

},

});

 

// 定义查询类型

// 查询有两个字段: contacts,可以用来获取数据库中的所有 contacts,而 contact 则根据 id 获取一个 contact 信息。 contact 字段允许所需的 id 参数为 graphqlid 类型。

var querytype = new graphql.graphqlobjecttype({

name: 'query',

fields: {

contacts: {

type: graphql.graphqllist(contacttype),

resolve: (root, args, context, info) => {

return new promise((resolve, reject) => {

database.all("select * from contacts;", function (err, rows) {

if (err) {

reject([]);

}

resolve(rows);

});

});

 

}

},

contact: {

type: contacttype, // 说明返回数据的类型

args: { // 定义期望从客户端得到的参数

id: {

type: new graphql.graphqlnonnull(graphql.graphqlid)

}

},

resolve: (root, { // 实际逻辑发生的地方

id // resolve()方法的第二个参数访问任何传递的参数

}, context, info) => {

// 简单调用 database.all() 和 database.run() 方法来执行正确的 sql 查询,以便从 sqlite 获取数据,返回一个 promise 来处理得到的数据。

return new promise((resolve, reject) => {

 

database.all("select * from contacts where id = (?);", [id], function (err, rows) {

if (err) {

reject(null);

}

resolve(rows[0]);

});

});

}

}

}

});

 

// 创建一个 mutation 类型,用于创建、更新和删除操作

var mutationtype = new graphql.graphqlobjecttype({

name: 'mutation',

fields: { // 所有的字段都接受符合 args 属性定义的参数,并由一个 resolve() 方法来获取传递过来的参数,执行相应的 sql 操作,并返回一个 promise。

createcontact: { // 创建 contacts

type: contacttype,

args: {

firstname: {

type: new graphql.graphqlnonnull(graphql.graphqlstring)

},

lastname: {

type: new graphql.graphqlnonnull(graphql.graphqlstring)

},

email: {

type: new graphql.graphqlnonnull(graphql.graphqlstring)

}

},

resolve: (root, {

firstname,

lastname,

email

}) => {

return new promise((resolve, reject) => {

database.run('insert into contacts (firstname, lastname, email) values (?,?,?);', [firstname, lastname, email], (err) => {

if (err) {

reject(null);

}

database.get("select last_insert_rowid() as id", (err, row) => {

 

resolve({

id: row["id"],

firstname: firstname,

lastname: lastname,

email: email

});

});

});

})

 

}

},

updatecontact: { // 更新 contacts

type: graphql.graphqlstring,

args: {

id: {

type: new graphql.graphqlnonnull(graphql.graphqlid)

},

firstname: {

type: new graphql.graphqlnonnull(graphql.graphqlstring)

},

lastname: {

type: new graphql.graphqlnonnull(graphql.graphqlstring)

},

email: {

type: new graphql.graphqlnonnull(graphql.graphqlstring)

}

},

resolve: (root, {

id,

firstname,

lastname,

email

}) => {

return new promise((resolve, reject) => {

database.run('update contacts set firstname = (?), lastname = (?), email = (?) where id = (?);', [firstname, lastname, email, id], (err) => {

if (err) {

reject(err);

}

resolve(`contact #${id} updated`);

 

});

})

}

},

deletecontact: { // 删除 contacts

type: graphql.graphqlstring,

args: {

id: {

type: new graphql.graphqlnonnull(graphql.graphqlid)

}

},

resolve: (root, {

id

}) => {

return new promise((resolve, reject) => {

database.run('delete from contacts where id =(?);', [id], (err) => {

if (err) {

reject(err);

}

resolve(`contact #${id} deleted`);

 

});

})

 

}

}

}

});

 

 

// 创建 graphql schema

// graphql schema 是 graphql 的核心概念,它定义了连接到服务器的客户端可用的功能。我们传递已定义的 query 和 mutation 类型到 schema。

const schema = new graphql.graphqlschema({

query: querytype,

mutation: mutationtype

});

 

app.use(

mount(

'/graphql',

graphqlhttp({

schema: schema,

graphiql: true, //是否开启本地调试模式

})

)

);

 

app.use(async ctx => {

ctx.body = 'hello world';

});

 

app.listen(3000,() => {

console.log("graphql server running at http://localhost:3000");

});

 

保存 index.js 文件,返回到你的 terminal。运行下列命令,启动服务器:

node index.js

 

 

怎样使用 graphql api

构建客户端前,可以使用 graphql 接口来测试你的 api。

访问网址 : http://localhost:4000/graphql ,并运行下列 mutation query:

mutation {

createcontact(firstname: "jon", lastname: "snow", email: "jonsnow@thenightswatch.com") {

id,

firstname,

lastname,

email

}

}

 

使用下列 mutation,可以更新 id 为 1 的 contact:

mutation {

updatecontact(id: 1, firstname: "aegon", lastname: "targaryen", email: "aegontargaryen@ironthrone.com")

}

 

也可以使用下列 mutation 来删除 id 为 1 的 contact:

mutation {

deletecontact(id: 1)

}

 

最后,使用如下 query,可以获取数据库中所有 contacts 信息:

query {

contacts {

id

firstname

lastname

email

}

}

 

使用下面 query,可以获取一个 contact 的信息:

query {

contact(id: 1) {

id

firstname

lastname

email

}

}

 

这里,我们得到的是 id 为 1 的 contact 信息。

 

 

 

 

四 vue和graphql

参考资料 使用vue和graphql构建一个crud app (改资料的修改有问题,本文已修复)

 

相关代码:前端从零开始学习Graphql前端从零开始学习Graphql

node服务: https://github.com/liuming888/graphql_node_demo.git
vue项目:
						https://github.com/liuming888/graphql_vue_demo.git
					
			

node服务复用上文进阶中的项目(下面采用koa服务)

打开koa-demo项目的server.js

增加一下代码

const cors = require('@koa/cors');

...

app.use(cors());

 

然后yarn add @koa/cors 再npm run dev

因为我们在两个不同的本地端口之间发送请求,这两个端口被视为两个独立的域,因此我们需要在服务器中启用跨源资源共享功能

 

创建一个 vue 项目

使用 vue cli,让我们继续创建一个 vue 项目。回到终端机,执行下列命令:

vue create vue-graphql-demo

当提示对预置选项进行选择时,你可以简单地选择默认设置。

等待生成项目,运行以下命令启动开发服务器:

cd vue-graphql-demo

npm run serve

这样,你的应用就运行起来了,在 http://localhost:8080/ 地址下可以访问。

 

安装 apollo 客户端

apollo 是一组实用程序,可以帮助你在 app 中使用 graphql。它以其客户端和服务器而闻名。apollo 是由 meteor development group开发和维护的:https://www.meteor.io/

打开一个新的终端,进入你的项目文件夹,运行以下命令来安装 apollo 客户端到你的 vue 项目中:

yarn add vue-apollo graphql apollo-boost

apollo boost 是一种无需任何手动配置就可以开始使用 apollo 客户端的方式。它包括一些常见的默认值,比如推荐的 inmemorycache 和 httplink,它们是以推荐的参数为你进行配置的。这种零配置的方式适合快速开发使用。

 

修改src/main.js 文件,代码如下:

import vue from 'vue';

import apolloclient from 'apollo-boost';

import vueapollo from 'vue-apollo';

import app from './app.vue';

import router from './router';

import store from './store';

import './registerserviceworker';

 

// 创建 apollo 客户端的一个实例,并传入 graphql 端点的 url

const apolloclient = new apolloclient({

uri: 'http://localhost:3000/graphql', // graphql服务的地址 (注意服务端开启cors)

});

 

vue.use(vueapollo); // 使用 vueapollo 插件将 apollo 集成到我们的 vue app 中

 

// 创建了 apollo provider,它包含所有 vue 组件都可以使用的 apollo client 实例。

const apolloprovider = new vueapollo({

defaultclient: apolloclient,

});

 

vue.config.productiontip = false;

 

new vue({

router,

store,

render: h => h(app),

apolloprovider, // 将 apollo provider 添加到 vue 实例中

}).$mount('#app');

 

使用 graphql api

将 vue-apollo 添加到 app 后,所有组件都可以通过 apollo 选项来使用 apollo

 

修改app.vue代码如下:

<template>

<div id="app">

<table border='1'

width='100%'

style='border-collapse: collapse;'>

<tr>

<th>first name</th>

<th>last name</th>

<th>email</th>

<th>actions</th>

</tr>

 

<tr v-for='contact in contacts'

:key="contact.id">

<td>{{ contact.firstname }}</td>

<td>{{ contact.lastname }}</td>

<td>{{ contact.email }}</td>

<td>

<input type="button"

@click="selectcontact(contact)"

value="select">

<input type="button"

@click="deletecontact(contact.id)"

value="delete">

</td>

</tr>

</table>

 

</br>

<form>

<label>first name</label>

<input type="text"

name="firstname"

v-model="firstname">

</br>

 

<label>last name</label>

<input type="text"

name="lastname"

v-model="lastname">

</br>

 

<label>email</label>

<input type="email"

name="email"

v-model="email">

</br>

 

<input v-if="!id"

type="button"

@click="createcontact(firstname, lastname, email)"

value="add">

<input v-if="id"

type="button"

@click="updatecontact(id, firstname, lastname, email)"

value="update">

<input type="button"

@click="clearform()"

value="clear">

 

</form>

</div>

</template>

 

<script>

import gql from "graphql-tag";

export default {

data() {

// 定义了四个组件变量,分别是 id、firstname、lastname 和 email。这些变量将被绑定到用于创建新联系人的 html form 中

return {

id: null,

firstname: "",

lastname: "",

email: ""

};

},

apollo: {

// gql 是一个 javascript 模板文字标签,它将 graphql 查询字符串解析到标准的 graphql ast 中。可以在 github 上的官方库中找到更多详细信息:

// https://github.com/apollographql/graphql-tag

 

// 在这个 apollo 对象中,我们添加了一个 contacts 属性,它将保存 contacts 查询的结果。稍后,我们将使用它在模板中显示联系人。

contacts: gql`

query {

contacts {

id

firstname

lastname

email

}

}

`

},

methods: {

// 添加 createcontact()、updatecontact() 和 deletecontact()

// 使用 this.$apollo.mutate() 方法向 graphql server 发送 mutation 查询,并调用 location.reload() 方法来重新加载页面。

 

createcontact(firstname, lastname, email) {

console.log(`create contact: ${email}`);

this.$apollo.mutate({

mutation: gql`

mutation createcontact(

$firstname: string!

$lastname: string!

$email: string!

) {

createcontact(

firstname: $firstname

lastname: $lastname

email: $email

) {

id

firstname

lastname

email

}

}

`,

variables: {

firstname: firstname,

lastname: lastname,

email: email

}

});

location.reload();

},

updatecontact(id, firstname, lastname, email) {

console.log(

"id, firstname, lastname, email: ",

id,

firstname,

lastname,

email

);

console.log(`update contact: # ${id}`);

this.$apollo.mutate({

mutation: gql`

mutation updatecontact(

$id: id!

$firstname: string!

$lastname: string!

$email: string!

) {

updatecontact(

id: $id

firstname: $firstname

lastname: $lastname

email: $email

)

}

`,

variables: {

id: id,

firstname: firstname,

lastname: lastname,

email: email

}

});

location.reload();

},

deletecontact(id) {

console.log(`delete contact: # ${id}`);

this.$apollo.mutate({

mutation: gql`

mutation deletecontact($id: id!) {

deletecontact(id: $id)

}

`,

variables: {

id: id

}

});

location.reload();

},

selectcontact(contact) {

this.id = contact.id;

this.firstname = contact.firstname;

this.lastname = contact.lastname;

this.email = contact.email;

},

clearform() {

this.id = null;

this.firstname = "";

this.lastname = "";

this.email = "";

}

}

};

</script>

 

 

 

<style lang="scss">

</style>

 

 

保存后,重新启动vue项目,会看到以下页面:

前端从零开始学习Graphql

 

到此,恭喜你已经成功入门graphql了,有兴趣可以自行查询相关资料深入研究

 

五 总结

使用graphql api的目的是什么?

创建api的目的是使自己的软件具有可以被其他外部服务集成的能力。即使你的程序被单个前端程序所使用,也可以将此前端视为外部服务,为此,当通过api为两者之间提供通信时,你能够在不同的项目中工作。

如果你在一个大型团队中工作,可以将其拆分为创建前端和后端团队,从而允许他们使用相同的技术,并使他们的工作更轻松。

优点

优点就是后端可以少招几个写接口的

前后端一起开发,节约工期

较少维护api文档,节省精力(代码即是文档)

说了这些,其实单对于前端来说,帮助不算特别大

缺点和难推广的地方

后端或者中间层把gql封装相应业务对接数据库是难点,需要高端人力

需要前端多少学一点类sql语句,不过大部分场景可以封装好固定的sql语句

封装gql不好会产生sql性能问题,三级嵌套联查还有n+1的老问题又会冒出来,需要持续优化

前端排除bug需要一定的后端知识,前后端架构多少了解一些

 

graphql比rest更好吗?

graphql是一种适合多种情况的方法。 rest是一种体系结构方法。如今,有大量的文章可以解释为什么一个比另一个好,或者为什么你应该只使用rest而不是graphql。另外你可以通过多种方式在内部使用graphql,并将api的端点维护为基于rest的架构。

你应该做的是了解每种方法的好处,分析自己正在创建的解决方案,评估你的团队使用解决方案的舒适程度,并评估你是否能够指导你的团队快速掌握这些技术。

本文更偏重于实用指南,而不是graphql和rest的主观比较。如果你想查看这两者的详细比较,我建议你查看另一篇文章,为什么graphql是api的未来

 

参考资料

2019 graphql学习指南    

使用 nodejs 创建一个 graphql 服务器

用node创建graphql api:代码量少,性能高

用node.js创建安全的 graphql api[每日前端夜话0x61]

koa + graphql 示例

graphql 入门详解

使用vue和graphql构建一个crud app