二十五、为什么选择 Relay 和 GraphQL?

在前一章中,我们介绍了 Flux 的体系结构原理。特别是,我们使用 Redux 库在 React 应用中实现了一些具体的流量概念。有一个像 Flux 这样的模式框架,可以帮助您分析应用中状态是如何变化和流动的,这绝对是一件好事。在本章末尾,我们考虑了一些规模方面的潜在限制。

在本章中,我将引导您了解在 React 应用中处理状态的另一种方法。与 Redux 一样,Relay 也用于 web 和移动应用。Relay 依赖于一种称为 GraphQL 的语言,该语言用于获取资源并对这些资源进行变异。

Relay 的前提是,它可以以 Redux 和其他处理状态的方法限制的方式进行扩展。它通过消除它们来做到这一点,并将重点放在组件的数据需求上。

在本书的最后一章中,我们将深入探讨一个非常流行的 Todo MVC 应用的 React 本机实现。

还有另一种方法吗?

这正是我在学习 Relay+GraphQL 时遇到的问题。然后我提醒自己 React 的美妙之处在于它只是 UI 的视图抽象;当然,会有很多方法来处理数据。所以真正的问题是,是什么让 Relay 比 Redux 更好或更差?

在较高的层次上,您可以将 Relay 视为通量架构模式的实现,并且可以将 GraphQL 视为描述封装在 Relay 中的通量存储如何工作的接口。在更实际的层面上,Relay 的价值在于易于实现。例如,使用 Redux,您有很多实现工作要做,只是用数据填充存储。这并不难做到,但随着时间的推移,它确实会变得冗长。正是这种冗长使得 Redux 很难扩展到某一点之外。

难以扩展的不是单个数据点。这是“大量获取请求最终生成非常复杂的存储”的聚合效应。Relay 允许您声明给定组件所需的数据,并让 Relay 找出获取此数据并将其与本地存储同步的最佳方法,从而改变了这一点。

在 React 应用中,Relay 方法是否比 Redux 和其他方法更好地处理数据?在某些方面,的确如此。它完美吗?远非如此。这是一个学习曲线,不是每个人都能摸索。它是不可变的,而且部分内容很难使用。然而,即使你决定反对,仅仅知道 Relay 方法的前提并看到它的实际应用也是值得的。

现在,让我们挑选一些词汇。

冗长的白话

在我开始深入研究数据依赖性和突变之前,我认为有必要先给出一些通用的 Relay+GraphQL 术语定义。

  • Relay:管理应用数据获取和数据突变的库,并提供将数据馈送到我们的应用组件的高阶组件
  • GraphQL:用于指定数据需求和数据突变的查询语言
  • 数据依赖性:一个抽象概念,表示给定的 React 组件依赖于特定的数据
  • 查询:查询是数据依赖的一部分,以 GraphQL 语法表示,由封装的 Relay 机制执行
  • 片段:较大 GraphQL 查询的一部分
  • 容器:RelayReact 组件,将获取的数据传递到应用 React 组件
  • 突变:一种特殊类型的 GraphQL 查询,它会改变某些远程资源的状态,Relay 必须在完成后找出如何在前端反映这种变化

困惑了吗?好的让我们快速讨论一下数据依赖关系和变化,以便我们可以查看一些应用代码。

声明性数据依赖关系

Relay 使用术语托管来描述位于使用数据的组件旁边的声明性数据依赖关系。这意味着我们不必到处寻找 action creator 函数,这些函数实际上获取分散在多个模块中的组件数据。通过托管,我们可以准确地看到组件需要什么。

让我们来看看这是什么样子。如果要显示用户的名字和姓氏,则需要告诉 Relay 您的组件需要此数据。然后,您可以放心,数据将始终存在于您的组件中。下面是一个例子:

const User = ({ first, last }) => ( 
  <section> 
    <p>{first}</p> 
    <p>{last}</p> 
  </section> 
); 

const UserContainer = Relay.createContainer(User, { 
  fragments: { 
    user: () => Relay.QL` 
      fragment on User { 
        first, 
        last, 
     } 
    `, 
  }, 
}); 

我们这里有两个部分。首先是User组件。这是实际呈现firstlast名称数据的 UI 元素的应用组件。请注意,这只是一个普通的旧 React 组件,呈现传递给它的道具。正如您在我们创建的UserContainer组件中所看到的,Relay 遵循您在本书前面了解的容器模式。在createContainer()函数中,我们通过传递 GraphQL 语法片段来指定该组件所需的数据依赖关系。

再一次,暂时不要详细讨论 Relay/图形 QL 的细节。这里的想法是简单地说明,这是我们需要编写的所有代码,以获得组件所需的数据。其余部分只是引导 Relay 查询机制,您将在下一章中看到。

突变应用状态

Relay 突变是在系统中引起副作用的行为,因为它们会改变 UI 所关心的某些资源的状态。Relay 突变的有趣之处在于,它们关心的是由于某种状态的改变而对数据产生的副作用。例如,如果更改用户的名称,这肯定会影响显示用户详细信息的屏幕。但是,它也可能影响显示多个用户的列表屏幕。

Relay 突变的想法是,您可以提前告诉他们应用可能会在哪些方面受到副作用的影响。由于其他组件可能会受到变异的影响,我们已经声明了它们的数据依赖关系,Relay 可以处理这类事情!令人惊叹的让我们看看突变是什么样子的:

class ChangeAgeMutation extends Relay.Mutation { 
  static fragments = { 
    user: () => Relay.QL` 
      fragment on User { 
        id, 
      } 
    `, 
    viewer: () => Relay.QL` 
      fragment on Viewer { 
        id, 
      } 
    `, 
  } 

  getMutation() { 
    return Relay.QL`mutation{changeAge}`; 
  } 

  getFatQuery() { 
    return Relay.QL` 
      fragment on ChangeAgePayload @relay(pattern: true) { 
        user { 
          age, 
        }, 
        viewer { 
          users, 
        }, 
     } 
    `; 
  } 

  getConfigs() { 
    return [{ 
      type: 'FIELDS_CHANGE', 
      fieldIDs: { 
        user: this.props.user.id, 
        viewer: this.props.viewer.id, 
      }, 
    }]; 
  } 

  getVariables() { 
    return { 
      age: this.props.age, 
      id: this.props.todo.id, 
    }; 
  } 
} 

哎呀,这是很多更改用户名的代码。其实,这并没有那么多,因为这包括处理副作用。让我们快速概述一下是什么导致了这种突变:

  • fragments:这些 GraphQL 片段在突变实际发生之前告诉突变组件使用了哪些数据。
  • getMutation():来自服务器的实际变异,将改变一些后端资源。
  • getFatQuery():这就是 Relay 如何确定执行该突变的副作用可能会影响到什么。例如,用户可能会更改,viewer.users集合也会更改。
  • getConfigs():这会告诉 Relay 我们将要执行的突变类型,以便它可以相应地计划。在本例中,我们只是更改一个属性值,但变异也可以从集合中添加或删除项,以此类推。
  • getVariables():发送到执行实际变异的后端 GraphQL 服务器的变异参数。

这只是突变本身的声明。在下一章中,我们将介绍如何执行实际的变异以响应某些事件,例如用户交互。

GraphQL 后端和微服务

到目前为止,我们讨论的关于 Relay 的所有内容都在浏览器中。Relay 需要将它的 GraphQL 查询发送到某个地方。为此,我们需要一个 GraphQL 后端。这非常容易实现,使用 Node.js 和一些 GraphQL 库。我们创建所谓的模式,描述所有将要使用的数据类型、查询和突变。

在浏览器中,Relay 通过降低数据流复杂性帮助您扩展应用。您可以声明所需的数据,而不必担心如何获取数据。实际需要解析此数据的是后端中的模式。

这是 GraphQL 帮助解决的另一个缩放问题。现代 web 应用是由微服务组成的。这些是更小的、自包含的 API 端点,用于比整个应用更小的特定用途(因此称为 micro)。我们的应用的工作是将这些微服务缝合在一起,并为前端提供有意义的数据。

同样,我们面临一个可伸缩性问题,我们如何维护由许多微服务组成的后端,而不引入无法克服的复杂性?这是 GraphQL 类型所擅长的。在下一章中,我们将开始使用后端 GraphQL 服务实现 Todo 应用。

总结

本章的目标是在本书的最后一章之前,快速向您介绍 Relay 和 GraphQL 的概念,我们将在这里实现一些 Relay/GraphQL 代码。

Relay 是 React 应用中解决状态管理问题的另一种方法。它的不同之处在于,它减少了与数据获取代码相关的复杂性,我们必须使用其他方法来编写这些代码,比如 Redux。

Relay 的两个关键方面是声明性数据依赖和显式变异副作用处理。所有这些都是通过 GraphQL 语法表示的。为了拥有 Relay 应用,您需要数据模式所在的 GraphQL 后端。现在,进入最后一章,我们将更详细地了解这些 Relay/图形 QL 概念。