Thinking In Relay #Facebook Relay文档翻译#

原文地址:Thinking In Relay
上一篇 Thinking in GraphQL
Relay文档翻译目录

Relay’s approach to data-fetching is heavily inspired by our experience with React. In particular, React breaks complex interfaces into reusable components, allowing developers to reason about discrete units of an application in isolation, and reducing the coupling between disparate parts of an application. Even more important is that these components are declarative: they allow developers to specify what the UI should look like for a given state, and not have to worry about how to show that UI. Unlike previous approaches that used imperative commands to manipulate native views (e.g. the DOM), React uses a UI description to automatically determine the necessary commands.
Relay获取数据的方案深受我们在React上工作经验的启迪。特别是,React将复杂的界面分割为可重用的组件,使得开发人员可以将应用很好地隔离为相对独立的单元,减少了应用中不相关部分的耦合。更重要的是这些组件是声明式的:允许开发者指定在什么状态下UI应该长什么样子,而且不用担心是如何显示成这个样子的。不像以前的方案需要命令式的要求操作本地视图(如DOM),React使用UI描述自动地生成所需的命令。

Let’s look at some product use-cases to understand how we incorporated these ideas into Relay. We’ll assume a basic familiarity with React.
让我们看一些产品中的例子,来说明我们是怎么把这些概念融入Relay的。我们假设您已经熟悉React了。

Fetching Data For a View 为一个视图获取数据

In our experience, the overwhelming majority of products want one specific behavior: fetch all the data for a view hierarchy while displaying a loading indicator, and then render the entire view once the data is ready.
在我们的经验中,绝大多数主流产品都有这样的一个行为:为一个层次结构的视图获取全部数据,与此同时显示加载指示器,当数据准备完毕时渲染整个试图。

One solution is to have a root component fetch the data for all its children. However, this would introduce coupling: every change to a component would require changing any root component that might render it, and often some components between it and the root. This coupling could mean a greater chance for bugs and slow the pace of development. Ultimately, this approach doesn’t take advantage of React’s component model. The natural place for specifying data-dependencies was in components.
一种方案是由一个根组件为它所有的子组件获取全部的数据。尽管如此,这将带来耦合问题:在某一个组件上的改动将会需要它所有的父节点都重新渲染一次,而且在它和父组件之间的也会受到影响。这个耦合增加了bug的几率而且放慢了开发的节奏。最终,这个方案没有发挥React组件的优势。最自然的方式应该是在组件本身上指定它需要什么数据。

The next logical approach is to use render() as the means of initiating data-fetching. We could simply render the application once, see what data it needed, fetch that data, and render again. This sounds great, but the problem is that components use data to figure out what to render! In other words, this would force data-fetching to be staged: first render the root and see what data it needs, then render its children and see what they need, all the way down the tree. If each stage incurs network request, rendering would require slow, serial roundtrips. We needed a way to determine all the data needs up-front or statically.
另一种合理的方案是使用render()作为方法来初始化数据获取。我们可以简单的渲染应用一次,看看需要什么数据,获取它,并再次渲染。听起来挺不错,但是问题是:组件用数据来指出什么需要被渲染。换言之,这将迫使数据获取分段:首先渲染根组件,然后看看需要什么数据,之后再渲染它的子组件,再看他们需要什么数据,这样的方法沿着树一直进行下去。如果每一个阶段都要求网络请求,渲染将会变慢,有很多次网络请求。我们需要一种方式可以知道所有需要的数据或者说静态的知道。

We ultimately settled on static methods; components would effectively return a query-tree, separate from the view-tree, describing their data dependencies. Relay could then use this query-tree to fetch all the information needed in a single stage and use it to render the components. The problem was finding an appropriate mechanism to describe the query-tree, and a way to efficiently fetch it from the server (i.e. in a single network request). This is the perfect use-case for GraphQL because it provides a syntax for describing data-dependencies as data, without dictating any particular API. Note that Promises and Observables are often suggested as alternatives, but they represent opaque commands and preclude various optimizations such as query batching.
我们最终设计了静态的方法;组件可以高效的返回一颗查询树,与视图树是不同的,它描述他们所依赖的数据。Relay将用这个查询树在一个阶段中获取所有需要的数据,并且用他们来渲染组件。问题的关键是找到一种合适的机制来描述该查询树,和一种高效的方式从服务器端获取数据(例如,在一次网络请求中完成)。这是应用GraphQL的完美场景,因为GraphQL提供了描述数据依赖作为数据的语法,不需要引入任何特定的API。注意,这些Promises和 Observables被建议作为备选,但是他们表示不透明的命令,并且提前包含了各种优化,例如批量查询。

Data Components aka Containers 数据组件又称为容器

Relay allows developers to annotate their React components with data dependencies by creating containers. These are regular React components that wrap the originals. A key design constraint is that React components are meant to be reusable, so Relay containers must be too. For example, a <Story> component might implement a view for rendering any Story item. The actual story to render would be determined by the data passed to the component: <Story story={ ... } />. The equivalent in GraphQL are fragments: named query snippets that specify what data to fetch for an object of a given type. We might describe the data needed by <Story> as follows:
Relay允许开发者为建立容器来为React组件以注释的方式描述数据依赖。这些容器也是普通的组件,包含着原来的组件。一个关键的设计理念是React组件是能够重用的,所以Relay容器必须也可以。例如,一个<Story>组件可以用来实现一个组件渲染任意的Story项。而实际被渲染的story是由传入组件的数据决定的<Story story={...} />。对应到GraphQL的是fragments:命名的查询片段,指定要获取什么数据一个对象的一个给定类型。我们可以描述<Story>的数据需求用以下的形式:

fragment on Story {
  text,
  author {
    name,
    photo
  }
}

And this fragment can then be used to define the Story container:
并且这个fragment可以用来定义Story容器。

// Plain React component.
// Usage: `<Story story={ ... } />`
class Story extends React.Component { ... }

// "Higher-order" component that wraps `<Story>`
var StoryContainer = Relay.createContainer(Story, {
  fragments: {
    // Define a fragment with a name matching the `story` prop expected above
    story: () => Relay.QL`
      fragment on Story {
        text,
        author { ... }
      }
    `
  }
})

Rendering 渲染

In React, rendering a view requires two inputs: the component to render, and a root DOM (UI) node to render into. Rendering Relay containers is similar: we need a container to render, and a root in the graph from which to start our query. We also must ensure that the queries for the container are executed and may want to show a loading indicator while data is being fetched. Similar to ReactDOM.render(component, domNode), Relay provides <RelayRootContainer Component={...} route={...}> for this purpose. The component is the item to render, and the route provides queries that specify which item to fetch. Here’s how we might render <StoryContainer>:
在React中,渲染一个视图需要两个输入:需要被渲染的组件和要被渲染到的DOM位置。渲染Relay容器也是类似:需要一个容器被渲染,和一个graph中的root用来指出从哪里开始查询。我们还需要确定容器中的查询被执行到,并且在数据获取过程中可以显示加载指示器。与ReactDOM.render(component, domNode)类似,Relay提供<RelayRootContainer Component={...} route={...}>来实现该目的。这个component就是要被渲染的组件,root表示提供的查询用来指出那些项需要被获取。下面是<StoryContainer>可能被渲染的形式:

ReactDOM.render(
  <RelayRootContainer
    Component={StoryContainer}
    route={
      queries: {
        story: () => Relay.QL`
          query {
            node(id: "123") /* our `Story` fragment will be added here */
          }
        `
      },
    }
  />,
  rootEl
)

RelayRootContainer can then orchestrate the fetching of the queries; diffing them against cached data, fetching any missing information, updating the cache, and finally rendering StoryContainer once the data is available. The default is to render nothing while data is fetching, but the loading view can be customized via the renderLoading prop. Just as React allows developers to render views without directly manipulating the underlying view, Relay and RelayRootContainer remove the need to directly communicate with the network.
RelayRootContainer可以开始获取这些查询的数据;与缓存中的数据比较,获取缺失的信息,更新缓存,最终当数据准备完毕时渲染StoryContainer。默认情况下数据获取过程中不进行任何渲染,但是加载视图可以通过renderLoadingprop定制化。就像React使得开发者渲染视图时不用直接操作视图一样,Relay和RelayRootContainer让开发者不用直接与网络打交道。

Data Masking 数据蒙板

With typical approaches to data-fetching we found that it was common for two components to have implicit dependencies. For example <StoryHeader> might use some data without directly ensuring that the data was fetched. This data would often be fetched by some other part of the system, such as <Story>. Then when we changed <Story> and removed that data-fetching logic, <StoryHeader> would suddenly and inexplicably break. These types of bugs are not always immediately apparent, especially in larger applications developed by larger teams. Manual and automated testing can only help so much: this is exactly the type of systematic problem that is better solved by a framework.
在典型的获取数据的方案中,我们发现两个组件之间有隐性依赖是很普遍的事。例如<StoryHeader>可能用到一些它并不去直接确定是否已经获取到的数据。这些数据通常被系统中的其他部分获取,如<Story>。之后当我们改变<Story>或者移除数据获取逻辑时,<StoryHeader>可能突然的、不可理解的出现问题。这样类型的bug通常不那么明显,特别是在多人开发的大型项目中。手工的或者自动化测试只能得出这样的结论:这样系统类型的问题最后是框架来解决。

We’ve seen that Relay containers ensure that GraphQL fragments are fetched before the component is rendered. But containers also provide another benefit that isn’t immediately obvious: data masking. Relay only allows components to access data they specifically ask for in fragments — nothing more. So if one component queries for a Story’s text, and another for its author, each can see only the field that they asked for. In fact, components can’t even see the data requested by their children: that would also break encapsulation.
我们已经看到了Relay容器确保GraphQL fragments在组件被渲染之前获取到数据。而且这容器还提供了额外的好处,虽然并不那么明显:数据蒙板。Relay只允许组件访问他们明确在fragments中生命的数据,其他的都不行。所以如果一个组件有两个查询,一个查Story的text,另一个查author,他们每一个只能看到自己请求的字段。事实上,组件甚至不能看到它包含的子组件请求的数据,查看子组件数据同样破坏封装。

Relay also goes further: it uses opaque identifiers on props to validate that we’ve explicitly fetched the data for a component before rendering it. If <Story> renders <StoryHeader> but forgets to include its fragment, Relay will warn that the data for <StoryHeader> is missing. In fact, Relay will warn even if some other component happened to fetch the same data required by <StoryHeader>. This warning tells us that although things might work now they’re highly likely to break later.
Relay走的更远:它在props上使用不透明的标识符,来验证在渲染组件以前我们已经明确的获得了数据。如果<Story>渲染<StoryHeader>,但是忘记包含他的fragment,Relay将警告<StoryHeader>的数据确实。事实上,Relay甚至当其他组件碰巧获取了<StoryHeader>所需的数据时也会发出警告。这些警告告诉我们虽然这些现在看起来能工作,它们很可能在将来出问题。

Conclusion 结论

GraphQL provides a powerful tool for building efficient, decoupled client applications. Relay builds on this functionality to provide a framework for declarative data-fetching. By separating what data to fetch from how it is fetched, Relay helps developers build applications that are robust, transparent, and performant by default. It’s a great compliment to the component-centric way of thinking championed by React. While each of these technologies — React, Relay, and GraphQL — are powerful on their own, the combination is a UI platform that allows us to move fast and ship high-quality apps at scale.
GraphQL提供了一个强大的工具用来构建高效、解耦的客户端应用。Relay提供了这样一个框架声明式的数据获取。将需要获取什么数据和怎么获取分开,Relay帮助开发者构建可靠、透明、高效的应用程序。它是以组件为中心一战成名的React的重要补充。当然React,Relay,GraphQL这些技术本身都是很强大的,他们的组合形成了一个UI 平台,使得我们可以行动更快更高效的生产高质量的应用程序

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值