graphql入门(二)
graphql 前端页面的应用
前端 graphql 简单应用
上次我们讲到了如何搭建一个简单的 graphql 中间层服务器,复习一下,那接下来我们就需要在前端调用这个服务。首先我们创建一个叫做 “fe-react” 的 react 项目:
1 | create-react-app fe-react && cd fe-react && npm run start |
浏览器会自动访问 localhost:3000,这样我们看到了我们熟悉的界面。
接下来我们需要安装 graphql 需要的一些依赖
1 | npm install apollo-boost react-apollo graphql |
依赖安装完成后我们来创建一个 apollo-client,这个 client 是为了连接我们的中间层并且请求数据用的,我们在 src 下面创建一个 graphql 的文件夹,用来管理我们的 graphql 请求,接着我们创建 client.js 文件,内容如下:
1 | import ApolloClient from 'apollo-boost'; |
这样我们的 client 就创建好了,接下来我们要开始请求数据了,在 src/graphql/文件夹下面创建 server.js 文件,内容如下:
1 | import { gql } from 'apollo-boost'; |
我们先看看这段代码,client.query 代表的是请求是 query 类型,传参为一个对象,对象中的 query 为请求主体,${gradeId}是使用了字符串模版,其实更合理的写法是增加一个变量
1 | export function getGrade(gradeId) { |
来我们看这一行:query getGrade($gradeId: Int!)
,变量及其类型需要在操作中申明,并且类型需要和请求定义的类型一致,用一个“$”来表示变量,如果有变量,那么需要给client.query增加variables的参数,对应在 graphql playground 中的操作为
1 | query getGrade($gradeId: Int!) { |
还需要在左下方的“QUERY VARIABLES”中增加
1 | { |
“QUERY VARIABLES”比较不起眼,在左下角,和它并列的还有“HTTP HEADERS”,注意不要搞混。
到这里大家可能会有个疑问query getGrade($gradeId: Int!)这句里面的“getGrade”是哪来的呢?它其实只是自己定义的一个操作名字,是为了更方便的查询日志和调试,虽然这个名字不影响功能,但是它的定义应该考虑其功能,不应该随便起个名字。下面是反面教材
1 | export function getGrade(gradeId) { |
改成这样功能一点不受影响,但是为调试和查找日志带来了很大的隐患。
上面这些呢就已经在前端定义了一个查询接口,我们直接在 src/app.js 中调用一下
首先引入方法
1 | import { getGrade } from './graphql/server'; |
在function App() { 下增加
1 | getGrade(1).then(res => { |
控制台就可以打印出数据,那我们接口就调用完成了,只要理解了 query,那 mutation 就可以轻易的举一反三。那么接下来介绍一下其他炫酷的写法
前端 graphql+react 的炫酷操作
ApolloProvider
这个东西可以将之前我们定义的那个client绑定到 react 组件的上下文中,我们改造我们的 src/app.js1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30import React from 'react';
import { ApolloProvider } from 'react-apollo';
import logo from './logo.svg';
import './App.css';
import { client } from './graphql/client';
function App() {
return (
<div className="App">
<ApolloProvider client={client}>
<header className="App-header">
<img src={logo} className="App-logo" alt="logo" />
<p>
Edit <code>src/App.js</code> and save to reload.
</p>
<a
className="App-link"
href="https://reactjs.org"
target="_blank"
rel="noopener noreferrer"
>
Learn React
</a>
</header>
</ApolloProvider>
</div>
);
}
export default App;是不是感觉没啥用?不要急,我们认识另一个新组件 Query
Query
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37import React from 'react';
import { ApolloProvider, Query } from 'react-apollo';
import { gql } from 'apollo-boost';
import logo from './logo.svg';
import './App.css';
import { client } from './graphql/client';
function App() {
return (
<div className="App">
<ApolloProvider client={client}>
<Query
query={gql`
query getGrade($gradeId: Int!) {
grade(gradeId: $gradeId) {
id
name
}
}
`}
variables={{
gradeId: 1
}}
>
{({ loading, error, data }) => {
if (loading) return <p>Loading...</p>;
if (error) return <p>Error :(</p>;
return <p>{data.grade.name}</p>;
}}
</Query>
</ApolloProvider>
</div>
);
}
export default App;需要注意的是 ApolloProvider 组件应该放到所有 Query 组件和 Mutation 组件的父级之中,最好呢放在最顶端,这样不需要考虑 client 是否注入到上下文中
这里只是做了一个简单的示例,如果有兴趣,请移步官方文档查看更多的用法,
本地缓存
apollo-client 从 v2.5 版本开始提供本地状态管理的功能,这一功能完全可以替换 redux,实现所有数据的来源统一。我们了解了 ApolloProvider 组件之后,再看 ApolloConsumer 组件就会容易理解,它其实是把 client 直接绑定到了上下文里面。我们在 src 下面新建一个 components 的文件夹用来存放自定义的一些组件,新建一个 showMore.jsx 的文件,内容如下
1 | import React from 'react'; |
紧接着创建 more.jsx,内容如下
1 | import React from 'react'; |
在 app.js 中引用两个组件
1 | import React from 'react'; |
在页面点击按钮的时候是不是会有变化
我们回过头总结一下发现我们通过client.writeData将数据写入到了缓存中,然后通过带有@client指令的字段从内存中获取到了数据。实际上我们更应该通过 mutation 来变更本地数据。我们应当添加我们本地的 resolvers。
我们修改 src/graphql/client.js 为下面这样
1 | import ApolloClient from 'apollo-boost'; |
首先我们初始化缓存中的showMoreStatus,然后在 resolvers 中定义了这个字段的 query 和 mutation,接着我们修改 src/components/showMore.jsx 文件为下面内容:
1 | import React from 'react'; |
这里我们对内存中数据的修改改成使用 mutation 的方式,注意操作本地需要@client
这样我们简易的本地缓存就搭建好了。根据上一节课我们讲到的 typeDefs 里面任何字段我们都可以重新实现一遍,那么我们也能猜到,任何字段我们都可以实现成本地缓存,只要在前端的 resolvers 中定义实现,那么请求时加上@client就会去本地获取,比如这个 query
1 | grade(gradeId: 1) { |
那我只需要在本地的 reolvers 中增加
1 | Grade: { |
即可。