题注:若是喜欢咱们的文章别忘了点击关注阿里南京技术专刊呦~ 本文转载自 阿里南京技术专刊-知乎,欢迎大牛小牛投递阿里南京前端/后端开发等职位,详见 阿里南京诚邀前端小伙伴加入~。前端
前段时间刷 Twitter 的时候看到大 V 纷纷提到 Apollo,预测它将在 2018 年崛起。正巧碰上有使用 GraphQL 的机会,在大概翻了下 Apollo 的文档以后,我下定决心在新的前端项目里尝试下抛开已经熟悉的 Redux,彻底使用 Apollo 来写数据层。一个月后的如今,我必须出来好好赞美下这位“太阳神”了。react
转眼已经 2018 年了,GraphQL 已再也不是个新鲜的名词了。15 年短暂的掀起一波讨论以后,彷佛也没有听到多少它的声音了。然而 Github 在这几年里慢慢成熟,Github 也将新版 api 彻底用 GraphQL 实现。在这里我就不展开讨论 GraphQL 的自己了,它让先后端之间的数据获取变得更加简单。redux
提到前端数据管理,最早想到的就是 Redux,我想不少人都体验过对 Redux 从陌生到熟悉的各个阶段,大体应该是这样的:后端
为何累了呢?由于 Flux 的单向数据流对你来讲已经再也不新鲜了。大部分时候,store 里存放的都是从后端请求来的数据,对于它们而言,怎么样作 dispatch 和 reduce 其实并非关键,反却是怎么设计 store 值得考虑。api
让咱们直接以一个真实的场景做为例子吧:promise
这是一个很常见的评论列表,拿到需求后咱们就开始写咱们的 <Comments />
组件了,在 Redux 的范式下,咱们不免要按照这个逻辑来写:缓存
Comments
的 didMount
里,dispatch
一个获取数据的 action
,在这个 fetch action
内发送请求。为了作 loading,咱们极可能要再 dispatch 一个 action 去通知 redux 咱们发起了一个请求。Comments
内咱们收到了 props 传来的数据,正式开始渲染咱们大量的工做花费在了如何获取数据上。而咱们面临的挑战又是什么呢?看几个产品经理们可能会提的需求bash
简单,从新请求一遍整个列表接口就行了!通常而言确实足够了,不过要求高的产品可能会要求你作”乐观“更新来让体验更好。这也没什么问题,加个 reducer
就是。数据结构
首先你会想,后端大哥能不能把这些字段都帮我加在评论的接口数据里,他坚决果断的拒绝了你,拿出一个 commonUser 的接口让你本身去调。细一想用户数据量不小,评论里也有大量的相同用户,不放在列表里也确实合理。心一横,干脆把前端这里的数据结构所有 normalize 化,按用户 id 为 key 用哈希表来存放数据。也就一个下午,你获得了一个很是完美的解决方案。架构
面对这样的场景,咱们写了太多的 命令式 代码,咱们一步步的描述了怎么去获取评论数据,在获得评论数据后再提取出全部的用户 id,去重后再次请求获取全部的用户数据,等等。咱们还须要考虑缓 normalize, 缓存,乐观更新等等细节上的问题。而这些,偏偏是 redux 帮不了咱们的。因而咱们会基于 Redux 封装更强大的库和框架,但真正 focus 在数据获取上的好像还真没看到很是合适的。
那么在 Apollo 的世界里是什么样的呢?
import { graphql } from 'react-apollo';
const CommentsQuery = gql`
query Comments() {
comments {
id
content
creator {
id
name
}
}
}
`;
export default graphql(CommentsQuery)(Comments);
复制代码
咱们使用了 graphql
(类比到 redux 中的 connect) 做为高阶组件将一条 GraphQL 的查询语句绑定到了 Comments 组件上,而后你全部的一切就准备就绪了。这么简单么?是的,咱们再也不须要描述怎么在 didMount 里发送请求,怎么处理请求来的数据。而是委托 Apollo 帮咱们处理这些全部事情,它会称职的帮咱们在须要的时候发送请求获取数据,而后将 data 映射到 Comments 的 props 中交给咱们。
不止于此,当咱们作更新操做的时候也会便捷许多。好比修改一条评论。咱们定义一个 graphql 的 mutation 操做:
// ...
const updateComment = gql`
mutation UpdateComment($id: Int!, $content: String!) {
UpdateComment(id: $id, content: $content) {
id
content
gmtModified
}
}
`;
class Comments extends React.Component {
// ...
onUpdateComment(id, content) {
this.props.updateComment(id, content);
}
// ...
}
export default graphql(updateComment)(graphql(CommentsQuery)(Comments));
复制代码
当咱们调用 updateComment 时,你就会神奇的发现,列表中的评论数据自动更新了。这是由于 apollo-client 把数据按照类型自动缓存在了 cache 中,GraphQL 节点返回的任何数据都会自动被用来更新缓存,在 UpdateComment 这个 mutation 中,咱们定义了它的返回值,一条类型为 Comment 的新修改评论,而且指定了须要接受的字段,content
和 gmtModified
。这样,apollo-client 就会自动经过 id 和类型去更新缓存中的数据,从而从新渲染咱们的列表。
再看看剩下的需求,咱们须要在鼠标停留在用户头像时展开用户详情。这个需求下咱们不只仅须要定义咱们须要什么数据,还会关心“怎么”获取数据(在 hover 头像时发送请求)。Apollo 一样为咱们提供了 “命令式” 的支持。
class UserItem extends React.Component {
// ...
onHover() {
const { client, id } = this.props;
client.query({
query: UserQuery,
variables: { id }
}).then(data => {
this.setState({ fullUserInfo: data });
});
}
}
export default withApollo(UserItem);
复制代码
幸运的是这里咱们依然不须要本身考虑缓存的问题。得益于 Apollo 全局的数据缓存,当咱们查询过用户 A 以后,再次查询相同 id 的数据会直接命中缓存,apollo-client 会直接 resolve 缓存中的数据,并不发送请求。这时候问题来了,假设我就是想要每次都从新查询呢?
client.query({
query: UserQuery,
variables: { id },
fetchPolicy: 'cache-and-network'
});
复制代码
Apollo 给咱们提供了不少策略来自定义缓存逻辑,好比默认的 cache-first
(优先使用缓存),这里的 cache-and-network
(先使用缓存,同时发请求更新),以及 cache-only
和 network-only
。
这些就是 GraphQL 和 Apollo 很吸引个人一些地方。当你开始从 GraphQL 的角度来思考,你更多的关心的是你的业务组件须要什么数据,而不是怎么一步步的得到它。而剩下的大部分业务场景,均可以经过前端的数据类型推导和缓存自动解决掉。固然,篇幅有限,还有不少优雅的地方来不及说起,好比分页,直接操做缓存达到乐观更新,轮询查询,以及数据订阅等等。若是有机会的话咱们能够继续深刻探讨。
看到这里,你可能会以为 “GraphQL 很酷,Apollo 也很酷,可是个人后端是 REST,目前是与他们无缘了”。其实否则,从 Apollo Client 的 2.0 版本开始引入了 Apollo Link,理论上来讲咱们能够经过 GraphQL 从任何类型的数据源获取数据。
“经过 GraphQL“ 意味着咱们可使用书写 GraphQL 的查询语句来获取不管是 rest api 或是 client state 中的数据,这样 Apollo Client 能够替咱们管理应用中全部的数据,包括缓存和数据拼接。
const MIXED_QUERY = gql`
query UserInfo() {
// graphql endpoint
currentUser {
id
name
}
// client state
browserInfo @client {
platform
}
// rest api
messages @rest(route: '/user/messages') @type(type: '[Message]') {
title
}
}
`;
复制代码
在这样一个 Query 查询中,咱们使用 GraphQL 的 directive 拼接了来自于 GraphQL,rest,client state 中的数据,将它们抽象在一块儿维护。与之相似的,咱们还能够封装相应的 mutation 实现。
以上大概就是我这段时间使用 Apollo 和 GraphQL 的一些浅浅的实践。虽然接触的不深,但我能够感觉到 Thinking in GraphQL 为前端带来的更优雅的解决方式,和 Apollo Client 这样一个完整的前端数据层解决方案的高效。我相信在 2018 年,它们会迎来更大的增加,甚至有代替 redux 成为通用数据管理方案的可能。
Apollo 相关的社区也比较活跃,在 dev-blog.apollodata.com 上也常常发表一些颇有参考价值的文章,有兴趣能够随便看看~