在 React 中使用 GraphQL

GraphQL 是一种基于 API 的查询语言,提供了一种可以描述的方式来获取我们想要的数据,它已经有了多种版本的实现,包括 JavaScript,那么今天我们就看一下在 React 项目中该如何使用 GraphQL。

首先来介绍一下我们所要使用的技术栈,前端页面使用 React,服务端使用 Koa,然后就是 Apollo GraphQL 了,一种基于 GraphQL 的解决方案集合。

项目结构

接下来看一下我们的这个小项目的文件结构,去除一些无关的文件,主要文件就是这些:

├── package.json
├── public
│   ├── bundle.js
│   └── index.html
├── server.js
├── src
│   ├── App.js
│   ├── index.html
│   └── index.js
├── webpack.dev.config.js
└── webpack.prod.config.js

其中 server.js 是我们的服务端逻辑,src 目录下是 React 相关的代码,public 目录下是打包后的静态文件,另外就是两个 webpack 配置文件。

创建 GraphQL 服务

下面来通过 Koa 来创建 GraphQL 服务,这里基于 Apollo Server,为我们提供了很方便的集成方式。

yarn add koa apollo-server-koa --dev

安装完依赖包后,就来看一下具体的实现吧,这里模拟了一些书籍的数据,创建了一个简单的 GraphQL 服务:

// 文件名为 server.js,文中提到的 server.js 指的就是这里的代码
const Koa = require('koa');
const { ApolloServer, gql } = require('apollo-server-koa');

// Mock Data
const BOOKS = [
{
id: 1,
title: 'HTML5 Tutorial',
author: 'xiaowang'
},
{
id: 2,
title: 'Deep Learning With React',
author: 'xiaoli'
}
];

// 创建数据模型
const typeDefs = gql`
type Book {
id: Int!
title: String
author: String
}
type Query {
books: [Book]
book(id: Int!):Book
}
`;

// 为数据模型创建解析函数
const resolvers = {
Query: {
// 查询全部数据
books() {
return BOOKS;
},
// 根据 id 查询单条数据
book(root, args) {
return BOOKS.filter(book => book.id === args.id)[0];
}
},
};

const server = new ApolloServer({ typeDefs, resolvers });

const app = new Koa();
server.applyMiddleware({ app });

const PORT = process.env.PROT || 4000;
app.listen(PORT, () => console.log(`App running in http://localhost:${PORT}`));

然后运行上面的代码,访问 http://127.0.0.1:4000/graphql,我们可以看到 GraphQL 为我们提供的 UI 界面:

左边提供了查询或修改语句的输入,右边会返回结果,同时我们还可以随时查看定义的数据模型,可以很方便的进行接口调试及验证。

上图中是查询全部的书籍信息,比如要查询某一本的书籍信息,输入如下语句即可:

query {
book (id: 1) {
id
title
author
}
}

数据的查询及变更

上面提到的都是数据查询,使用的是操作符 query,那么对于数据的变更该如何实现呢?GraphQL 是通过 mutation 操作符来实现数据变更,也就是说可以用这个操作符实现添加、修改、或者删除数据。

现在来改写一下 server.js 里的代码,添加数据的修改及删除功能。

添加 Mutation 类型

首先是添加数据模型,这里包括了 addBook, updateBook, deleteBook 方法,分别代表添加书籍、修改书籍和删除书籍。

// 数据模型
const typeDefs = gql`
type Book{
id: Int!
title: String
author: String
}
type Query {
books: [Book]
book(id: Int!):Book
}
type Mutation {
addBook(title: String!, author: String!):Book
updateBook(id: Int!, title: String, author: String):Book
deleteBook(id: Int!):Book
}
`;

然后添加响应的解析函数,并且每次操作都会返回操作的结果:

// 模拟添加数据时 id 自增
let incrementID = 2;

const resolvers = {
// 数据查询
Query: {
books() {
return BOOKS;
},
book(root, args) {
return BOOKS.filter(book => book.id === args.id)[0];
}
},
// 数据变更
Mutation: {
addBook(root, args) {
let newBook = {id: ++incrementID, ...args};
// fake add book
BOOKS.push(newBook);
return newBook;
},
updateBook(root, args) {
let modifiedBook = {};
// fake update book
BOOKS.forEach(book => {
if (book.id === args.id) {
Object.assign(book, args);
modifiedBook = book;
}
});
return modifiedBook;
},
deleteBook(root, args) {
let deletedBook = {};
BOOKS.forEach((book, i) => {
if (book.id === args.id) {
BOOKS.splice(i, 1);
deletedBook = book;
}
});
return deletedBook;
}
}
};

此时,数据的变更逻辑已经处理完毕了,我们可以现在 GraphQL 的 UI 界面里测试一下效果,比如添加一本书籍,输入如下语句即可:

mutation {
addBook (title: "Hello GraphQL", author: "Tom") {
id
title
author
}
}

执行后会返回成功信息,同时会返回添加的数据,对于本示例的更多的操作语句可以参考这里: https://github.com/bluesdari/react-graphql-starter/blob/master/README.md

在 React 中使用

对于 React 基础环境的搭建,可以使用 create-react-app 进行创建,也可以自行配置,由于本项目比较简单,这里采用了自行配置的方式,具体配置可以参考文末提供的代码仓库地址,这里不再赘述。

搭建完 React 的基础环境成之后,接下来要安装 GraphQL 相关的依赖:

yarn add apollo-boost graphql @apollo/react-hooks

创建 QraphQL 客户端

这里我们通过 Apollo Boost 来创建,实例化一个 ApolloClient 并指定 GraphQL 的服务地址。然后用 ApolloProvider 组件把原来的组件包裹起来,类似于 Redux 中的 Provider

// index.js
import React from 'react';
import { render } from 'react-dom';
import ApolloClient, { InMemoryCache } from 'apollo-boost';
import { ApolloProvider } from '@apollo/react-hooks';

import App from './App.js';

const client = new ApolloClient({
uri: 'http://127.0.0.1:4000/graphql',
cache: new InMemoryCache(),
});

render(
<ApolloProvider client={client}>
<App />
</ApolloProvider>,
document.getElementById('root')
);

在 React 中获取数据

然后我们在 App.js 来实现一个数据查询的功能,声明查询语句,并用 useQuery 来实现数据查询的逻辑。

// App.js
import React from 'react';
import { gql } from 'apollo-boost';
import { useQuery } from '@apollo/react-hooks';

const GET_BOOKS = gql`
{
books {
id
title
author
}
}
`;

const App = () => {
const { loading, error, data } = useQuery(GET_BOOKS);

if (loading) return 'Loading...';
if (error) return `Error! ${error.message}`;

return (
<div>
<p>Book Lists</p>
<ul>
{data.books.map(book => (
<li key={book.id}>
{book.id}, {book.title}, {book.author}
</li>
))}
</ul>
</div>
);
};

export default App;

启动项目

对于项目的开发环境和生产环境,做了如下的配置:

{
"start": "node server.js & npm run webpack-dev",
"webpack-dev": "webpack-dev-server --config ./webpack.dev.config.js --hot",
"build": "webpack --config ./webpack.prod.config.js",
"serve": "node server.js"
},

在开发环境使用 npm start 启动,会运行两个服务,一个是 node 服务,提供 GraphQL 接口,另外一个是 webpack 开发环境。

对于生产环境我们可以使用 npm run build 进行项目打包,然后运行 npm run serve 即可。

以上就是关于在 React 中使用 GraphQL 的一些简单介绍,希望能够起到抛砖引玉的作用,本文还提供了完整的代码示例供大家参考:https://github.com/bluesdari/react-graphql-starter

2019-11-12 更新:

  • 升级 React 到最新版,支持 Hooks
  • Babel6 升级到 Babel7
  • Apollo 相关依赖升级到最新版
  • Webpack 构建支持生产环境