学习文档
客户端
在前端使用GraphQL API是开发新抽象并帮助在客户端实现通用功能的绝佳机会。让我们考虑一些您可能希望在应用程序中拥有的"基础设施"功能:
- 直接发送查询和突变,无需构造HTTP请求
- 整合视图层
- 缓存
- 基于
schema的查询校验和优化
当然,没有什么能阻止你使用纯HTTP来获取数据,然后自己移动所有位,知道正确的信息最终出现在你的UI中。但是,GraphQL提供了将你在该过程中通常必须做的大量手动工作抽象的能力,让你专注于应用中真正重要的部分。 在下文中,将会更详细地讨论这些任务是什么.
目前有两个主要的GraphQL客户端可用:
第一个是Apollo Client,它是一个社区驱动的努力成果,旨在为所有主流平台构建一个强大而灵活GraphQL客户端。
第二个较多Relay,它是Facebook的本土GraphQL客户端,并且只在web上可用。
直接发送queries和mutations
GraphQL的一个主要好处是它允许你以声明的方式获取和更新数据,换句话说,我们在API抽象阶梯上更上一层楼,不再需要自己处理低级网络人物。
像之前使用纯HTTP请求的方式从API加载数据,使用GraphQL你只需要在查询的地方声明你的数据需求并让系统负责发送请求和处理相应,这正式GraphQL客户端要做的事情。
查看层集成和ui更新
一旦GraphQL客户端接收并处理了服务器相应,请求的数据就需要以某种方式最终出现在UI中。根据开发正在使用的平台和框架,通常会有不通的方法来处理UI更新。
以React为例,GraphQL客户端使用高阶组建的概念在后台获取所需要的数据,并使其在组件的props中可用 。一般来说,GraphQL的声明行本质与函数式响应技术编程技术(functional reactive programing)密切相关。这两者形成了一个强大的组合,其中视图简单的声明其数据依赖关系,并且UI与选择的FRP层连接起来
缓存查询结果概念及策略
在大多数应用程序中,您需要维护先前从服务器获取的数据的缓存。在本地缓存信息对于提供流畅的用户体验以及减轻用户数据计划的负担至关重要。
通常,在缓存数据时,直觉是将远程获取的信息放入本地存储中,以便以后检索。使用 GraphQL,最简单的方法是将 GraphQL 查询的结果简单地放入存储中,并在发送相同查询时简单地返回它们。事实证明,这种方法对于大多数应用程序来说效率很低。
一种更有益的方法是预先对数据进行标准化。这意味着(可能嵌套的)查询结果被展平,并且存储将仅包含可以使用全局唯一 ID 引用的单个记录。如果您想了解更多有关此内容的信息,Apollo 博客上有一篇关于该主题的精彩文章。
构建时schema校验及优化
由于模式包含有关客户端可以使用 GraphQL API 做什么的所有信息,因此有很好的机会来验证和可能优化客户端希望在构建时发送的查询。 当构建环境可以访问模式时,它基本上可以解析位于项目中的所有 GraphQL 代码,并将其与模式中的信息进行比较。这会在应用程序进入实际用户手中之前捕获拼写错误和其他错误,而错误的后果会更加严重。
共置视图和数据依赖关系
GraphQL 的一个强大概念是它允许您同时拥有 UI 代码和数据需求。视图及其数据依赖的紧密耦合极大地改善了开发者的体验。消除了思考正确数据如何最终出现在 UI 的正确部分的心理开销。
托管工作的效果取决于您正在开发的平台。例如,在 Javascript 应用程序中,实际上可以将数据依赖项和 UI 代码放入同一个文件中。在 Xcode 中,Assistant Editor 可用于同时处理视图控制器和 graphql 代码。
服务端
GraphQL 通常被解释为一种以前端为中心的 API 技术,因为它使客户端能够以比以前更好的方式获取数据。但是 API 本身当然是在服务器端实现的。服务器也有很多好处,因为 GraphQL 使服务器开发人员能够专注于描述可用的数据,而不是实现和优化特定的端点。
graphql执行
GraphQL 不仅指定了描述模式的方法和从这些模式中检索数据的查询语言,还指定了如何将这些查询转换为结果的实际执行算法。该算法的核心非常简单:查询逐个字段遍历,为每个字段执行“解析器”。所以,假设我们有以下模式:
type Query {
author(id: ID!): Author
}
type Author {
posts: [Post]
}
type Post {
title: String
content: String
}
以下是我们可以发送到具有该模式的服务器的查询:
query {
author(id: "abc") {
posts {
title
content
}
}
}
首先要看到的是查询中的每个字段都可以与一个类型相关联:
query: Query {
author(id: "abc"): Author {
posts: [Post] {
title: String
content: String
}
}
}
现在,我们可以轻松地在我们的服务器中找到为每个字段运行的解析器。执行从查询类型开始,并以广度优先。这意味着我们首先运行 Query.author 的解析器。然后,我们获取该解析器的结果,并将其传递给它的子节点,即 Author.posts 的解析器。在下一级,结果是一个列表,因此在这种情况下,执行算法一次运行一个项目。所以执行是这样的:
authorLoader = new AuthorLoader()
// Queue up a bunch of fetches
authorLoader.load(1);
authorLoader.load(2);
authorLoader.load(1);
authorLoader.load(2);
// Then, the loader only does the minimal amount of work
fetch('/authors/1');
fetch('/authors/2')
我们还能做得更好吗?是的,如果我们的 API 支持批量请求,我们只能对后端进行一次提取,如下所示:
fetch('/authors?ids=1,2')
这也可以封装在上面的loader中. 在 JavaScript 中,可以使用名为 DataLoader 的实用程序来实现上述策略,其他语言也有类似的实用程序。