在 2015 React 欧洲大会上,Lee Byron 介绍了 Facebook 的 GraphQL ,包含 GraphQL 背后的故事,查询语句的示例,还有核心的概念。GraphQL 非常易懂,直接看查询语句就能知道查询出来的数据是什么样的。你可以把 GraphQL 的查询语句想成是没有值,只有属性的对象,返回的结果就是对应的属性还有对应值的对象。
故事
从 2011 开始,Facebook 开始越来越重视移动端,一支很小的团队开始去做 Android 与 iOS 应用。Facebook 的强项是 Web,也非常的了解 Web ,而且在这方面储备了大量的技术。当年 Facebook 的主要平台就是传统的 浏览器 Web 服务器 数据服务 的组合,Web 服务器响应浏览器的请求,到数据服务那里提供出数据,然后再交给浏览器去显示。
他们打算尽可能的使用现有的代码去实施移动端的应用,所以一开始 Facebook 的移动应用就是一个浏览器,加上了一个本地的壳,内容基本上就是简单的定制以后的移动 Web 网站。这样的好处就是可以使用所有的现有的 Web 平台上的东西。这样工程师们也可以使用平时创建东西的方法。这种方法在短时间内也得到了很大的成功,并且让公司把重点放在移动端上。
一开始都还好,不过在移动应用上添加越来越多的功能以后,就有点吃力了,移动浏览器经常会消耗掉所有的内存,让应用崩溃。另一面,在 Web 上,Facebook 仍然快速的生成,而移动端有点跟不上脚步了。这让他们决定要去做真正的本地的移动应用。
2012 年开始,Facebook 要开始开发真正的本地应用。 这跟 Web 很不一样,所以开始重新思考应用的平台。Web 就是请求一个 URL ,返回一堆 HTML。而本地移动应用,为了给应用提供需要的数据,填充数据模型 ,显示视图,要想的问题是,怎么去请求,准备,传递这些数据。而当时 Facebook 现有的服务器主要功能还是只提供 HTML。
工程师们试了一些方法,比如 RESTful API,对于 Facebook 这种复杂的应用,可能需要定义很多的端点,不同的端点返回来的数据只是略有不同,造成了资源浪费,而且还需要大量的逻辑去处理这些数据。后来他们又试了 FQL, 这是 Facebook 的公共接口,应该是一种查询语言。功能很强大,而且返回来的数据也有很好的结构。不好的地方是,查询用的语言非常难理解,比如多个 JOIN ,主键什么的,所以经常会出错。
除了这些表面上遇到的问题,工程师们也非常不喜欢这些方法表达数据的形式,比如我们平时想像的数据并不是一大堆查询语言,LEFT JOIN,RIGHT JOIN .. 也不是资源的地址。而对象的形式非常适合表达数据,一个对象,里面有一些属性,不同的属性对应不同的值。几个工程师开始了现在的 GraphQL,一种用对象,属性,关系的,有点像图形的方式来表达想要的数据。
三年前,Facebook 用了 GraphQL 做了第一款真正的本地移动应用,现在,应用每天会接受 260 亿的请求。
查询示例
下面通过几个示例来看一下 GraphQL 的查询是什么样的。
{
me {
name
}
}上面这段就是一个 GraphQL 的查询语句,非常的容易懂。有点像 JSON 形式,比如一组大括号,里面是有点像对象的东西,这些叫做选择集,然后还有一些看起来像属性的东西,这些叫字段。不同的字段可以包含自己的选择集,这样可以去描述嵌套的查询。把这种形式的查询,用字符的形式,发送给 GraphQL 服务器,服务器解析,执行查询,然后返回来的东西是这样的:
{
"me": {
"name": "wanghao"
}
}返回来的数据应该就是 JSON。注意这里返回来的数据跟查询用的语言是非常像的,你会看到结构基本是一样的。这让 GraphQL 非常容易学习跟使用。用 GraphQL 的查询来描述要查询的结果数据的形状。在上面,name 是用户对象里的一个字段, me 有点像是一个起点,表示查询用户。
下面是另一种查询方法:
{
user(id: 6) {
name
}
}在上面这个查询里, id 是字段的一个参数,这些参数可以让查询返回不同的数据。我们可以把字段想成是一个可以返回数据的函数。再来看一个复杂点的查询:
{
me {
name,
profilePicture {
width,
height,
url
}
}
}
上面的查询要的是用户相关的数据,指定了 name 字段,也就是用户,还有 profilePicture 用户头像的字段,这个字段又包含了一个选择集,然后在这里又指定了需要 width 宽度,height 高度,还有 url 地址这几个字段。
返回来的结果像这样:
{
"me" {
"name": "wanghao,"
"profilePicture": {
"width": 50,
"height": 50,
"url": "https://cdn/some.jpg"
}
}
}用户头像可能会有多个尺寸,我们也可以查询特定尺寸的用户头像,方法就是指定一个参数还有对应的值,这里就是指定了一个 size 参数,值设置成了 300 :
{
me {
name,
profilePicture(size: 300) {
width,
height,
url
}
}
}
返回的结果像这样:
{
"me" {
"name": "wanghao,"
"profilePicture": {
"width": 300,
"height": 300,
"url": "https://cdn/300.jpg"
}
}
}在查询里面,可以多次查询同一个字段,然后使用别名,还有参数去影响返回的结果:
{
me {
name,
littlePic: profilePicture(size: 50) {
width,
height,
url
},
bigPic: profilePicture(size: 300) {
width,
height,
url
}
}
}
上面的查询时,两次查询 profilePicture 字段,不同指定了不同的别名,一个是 littlePic ,另一个是 bigPic,一个返回小图像,一个返回大图像。返回的结果看起来像这样:
{
"me" {
"name": "wanghao,"
"littlePic": {
"width": 50,
"height": 50,
"url": "https://cdn/50.jpg"
},
"bigPic": {
"width": 300,
"height": 300,
"url": "https://cdn/300.jpg"
}
}
}我们可以嵌套查询,比如查询用户的朋友,像这样:
{
me {
name,
friends {
name
}
}
}上面指定了 friends 字段,这个字段有自己的选择集,就是一个 name 字段,返回的结果:
{
"me": {
"name": "wanghao",
"friends": [
{
"name": "zhangsan"
},
{
"name": "lisi"
}
]
}
}friends 就是朋友的列表,返回来的就是一个数组,里面是一些对象,对象有一个 name 属性。嵌套可以是有任意的深度,比如不但要朋友列表的用户名,还需要朋友参加的活动的名字:
{
me {
name,
friends {
name,
events {
name
}
}
}
}返回的结果:
{
"me": {
"name": "wanghao",
"firends": [
{
"name": "zhangsan",
"events": [
{
"name": "青岛啤酒节"
}
]
},
{
"name": "lisi",
"events": [
{
"name": "青岛啤酒节"
}
]
}
]
}
}在查询里也可以做一些限制,比如排序的标准,或者限制返回来的结果的数量:
{
me {
name,
friends(orderby: IMPORTANCE, first: 1) {
name,
events(first:1) {
name
}
}
}
}组合
GraphQL 可以组合使用查询。比如可以定义一种叫 fragment 的东西,就是查询片断,然后我们可以在不同的地方重复的去使用查询。比如下面的这个例子:
{
me {
name
friends {
name
events {
name
}
}
}
}可以转换成这样:
{
me {
name
friends {
...firendFragment
}
}
}
fragment friendFragment on User {
name
events {
name
}
}上面定义了一个叫 friendFragment 的查询片断,它返回用户朋友的名字,还有参加的活动的名字,然后我们可以在其它的查询里面使用这个查询片断。
在下面这个例子里,定义了 profilePicture 这个查询片断,然后我们在一个 React 的组件里用到了它。
fragment profilePicture on User {
profilePicture {
width,
height,
url
}
}
class ProfilePicture extends React.Component {
render() {
var pic = this.props.data.profilePic;
return <img src="https://ninghao.net/%7Bpic.url%7D" alt="" width="{pic.width}" height="{pic.height}" />
}
}这些查询片断也可以组合在一起使用,比如在下面的 PersonRow 这个查询片断里,用到了 profilePicture 这个查询片断。
fragment personRow on User {
...profilePicture,
name,
isFriend
}
class PersonRow extends React.Component {
render() {
var data = this.props.data;
return (
<div>
<ProfilePicture data={data} />
{data.name}
{data.isFriend ? null : <AddFriendButton />}
</div>
);
}
}
更多组合的例子:
{
event(id: 123) {
...attendeeList
}
}
fragment attendeeList on Event {
attendees {
...personRow
}
}
fragment personRow on User {
...profilePicture,
name,
isFriend
}
fragment profilePicture on User {
profilePicture {
width,
height,
url
}
}核心
GraphQL 语言的核心观念就是我们平时怎么去考虑数据, 几乎可以通过查询就知道查询的结果是什么样的。GraphQL 的核心是一个 Type System,用它去描述服务器的所有的功能。每一个级别的 GraphQL 查询,都会应用某个特定的 Type,这些 Type 描述了有哪一些字段是可用的,字段上能够使用的参数都有哪些,结果的类型是什么等等。下面是一个 Type 示例:
type Query {
me: user
user(id: Int): User
}
type User {
name: string
profilePicture(size: Int = 50): ProfilePicture
friends(first: Int, orderby: FriendOrderEnum): [User]
events(first: Int): [Event]
}
enum FriendOrderEnum {
FIRST_NAME,
LAST_NAME,
IMPORTANCE
}
type ProfilePicture {
width: Int
height: Int
url: String
}
type Event {
name: String
attendees(first: Int): [User]
}
me 还有 user ,都会返回 User 这个 Type。上面我们用的是 me ,他会返回 User,这样我们就可以使用 User 这个 Type 里面描述的字段,比如名字,头像,好友列表等等。类型系统里不仅可以指定字段,还有字段可用的参数。比如 orderBy 就是一个参数,这个参数指定三种可能的值,这些值是用 FirendOrderEnum 描述的。
ProfilePicture 返回 ProfilePicture 这个 Type,width 与 height 这两个字段是整数类型的,url 是字符串类型的数据。如果字段返回的是一个列表,可以使用一组方框号,比如 friends 返回的是用户的列表,它的值就是 [User] 。
利用您现有的技术
GraphQL 允许你使用自己已有的技术平台,它并不关心你的后台到底用的是啥。GraphQL 并不是一个数据引擎,他不存储数据。我觉得有点像是一个查询解释的工具,你可以去定义一套 Type System ,创建一些 Type,描述一下服务器的功能,比如能够返回的数据,每个 Type 里的字段有点像是一个函数,你可以在里面定义返回的数据。
GraphQL



评论
不错支持,随便我还用不到
10 年 2 个月 以前
Facebook 的移动应用都是用这玩意提供数据。
10 年 2 个月 以前
好像明白,又好像糊涂了~
正在学你的 《Semantic UI 应用接口》,有点不关系。以前都一步一步照着照的~
这个视频第二节讲了准备。
学第三节课才发现自己没准备。哈哈~
10 年 2 个月 以前
GraphSQL
10 年 2 个月 以前
我读了一半,看到文章里有几处可以修改的地方:
1. "越来越重要移动端"应该是"越来越重视移动端"
2. 选择集里面的字段"firends"应该是"friends"
3. "然后我们可以在不同的地方重要的去使用查询"这样说"在选择集里,重要的地方可以使用fragment"是不是更好一点?
4. "还有参数的活动的名字"应该是"还有参加活动的名字"
9 年 1个月 以前
感谢啊 :)
9 年 1个月 以前
好文章就要帮助完美一点嘛:)
9 年 1个月 以前
有计划出一期关于 GraphQL 和 Relay 的教程吗?
8 年 7 个月 以前