使用 Apollo 客户端的片段组合:约定和样板

Posted

技术标签:

【中文标题】使用 Apollo 客户端的片段组合:约定和样板【英文标题】:Fragment composition with Apollo client: convention and boilerplate 【发布时间】:2018-01-09 12:53:11 【问题描述】:

在 Apollo 应用程序(还有 GraphQL/Relay)中,可以选择将数据需求与组件并置,或者最终自己组装大型 GraphQL 查询。我们选择将数据需求与组件并置是因为我们希望长期具有更好的可维护性,因为您无需查看整个组件树或页面即可查看所有数据需求,并且可以在本地添加新需求。

我想更好地了解如何使用 Apollo 客户端编写 GraphQL 片段。我知道该怎么做,但我想知道如何才能做得更好。

目前,编写我的片段涉及大量样板文件,尤其是当我的组件只是传递未触及的属性时。

片段声明约定?

首先,我们来看一个简单的组件:

export const User = (
  user: 
    firstName,
    lastName,
    job,
    email,
    pictureUrl,
    color
  ,
  ...props
) => (
  <UserWrapper ...props>
    <UserAvatarWrapper>
      <Avatar
        firstName=firstName
        lastName=lastName
        color=color
        src=pictureUrl
      />
    </UserAvatarWrapper>
    <UserContentWrapper>
      (firstName || lastName) &&
        <UserName>
          firstName
          " "
          lastName
          " "
          email && <UserEmailInline>email</UserEmailInline>
        </UserName>
      job && <UserJob>job</UserJob>
    </UserContentWrapper>
  </UserWrapper>
);
User.fragments = 
  user: gql`
      fragment User on User 
          id
          firstName
          lastName
          pictureUrl: avatar
          job
          color
          email
      
  `,
;

这里有一些选择。大多数示例中似乎都使用了某种约定,但该约定在文档中并未明确。

User.fragments 上使用的密钥。将其命名为与组件的 propName user 完全相同是否有意义?

片段的名称:按照惯例,人们似乎用组件的名称来命名它,如果有用,请在片段上加上 GraphQL 类型的后缀。 (这里的UserUser 可能是过分的后缀)。

我认为在同一个应用程序中遵循相同的约定是很好的,这样所有片段声明都是一致的。那么,有经验的人能帮我澄清一下这个似乎在许多 Apollo 示例中使用的约定吗?

减少片段组成样板?

现在让我们考虑一个遵循我们设置的约定的Relationship 组件。

const Relationship = ( user1, user2 ) => (
  <RelationshipContainer>
      <RelationshipUserContainer>
        <User user=user1 />
      </RelationshipUserContainer/>
      <RelationshipUserContainer>
        <User user=user2 />
      </RelationshipUserContainer/>
  </RelationshipContainer>
);
Relationship.fragments = 
  user1: gql`
      fragment RelationshipUser1User on User 
          ...User
      
      $User.fragments.user
  `,
  user2: gql`
      fragment RelationshipUser2User on User 
          ...User
      
      $User.fragments.user
  `,
;

请注意,我在这里声明了 2 个看起来相同的片段。我认为这是必要的,因为有 2 个道具,您不必假设两个道具的数据要求相同。我们可以很容易地想象一个带有me 道具和friend 道具的组件,您将在其中收到更多me 道具的数据。

这很好用,但它有相当多的样板和中间片段看起来非常不必要。此外,这并不总是很方便,因为从组件用户的角度来看,您必须了解 2 个片段名称才能使用它。

我试图用以下方法简化这一点

Relationship.fragments = 
  user1: User.fragments.user,
  user2: User.fragments.user,
;

这可以工作,但如果你这样做,那么片段名称不再是RelationshipUserXUser,而是User,这意味着它破坏了封装,并且你需要以某种方式在内部意识到@987654333 @ 组件正在使用User 组件。

如果有一天,Relationship 组件切换到使用像 UserAlt 这样的替代表示,这将需要使用关系片段从所有组件中重构,这是我想避免的事情。我认为在这种情况下,修改应该只发生在Relationship 组件中。

结论

我想知道使用 Apollo 组合片段的最佳实践,以便组件保持真正封装,最好不要涉及太多样板。

我已经在做正确的事了吗?

如果我真的想编写查询,这些样板文件是不可避免的吗?

【问题讨论】:

【参考方案1】:

这样做怎么样:

const userFragment = gql`
  fragment Relationship_user on User 
    ...User_user
  
  $User.fragments.user
`;
Relationship.fragments = 
  user1: userFragment,
  user2: userFragment,
;

除此之外,我建议您将片段名称的范围设置为如上所示,因为需要某种名称间距,否则您很可能会遇到两次使用相同片段名称的情况。

User.fragments.user => User_user Relationship.fragments.user => Relationship_user

【讨论】:

谢谢,如果它改进了代码库,我会尝试遵循这些建议并在这里报告 我开始使用这个约定,因为它似乎很有意义。顺便说一句,这似乎也是本文中使用的约定:medium.com/@wonderboymusic/…

以上是关于使用 Apollo 客户端的片段组合:约定和样板的主要内容,如果未能解决你的问题,请参考以下文章

启用缓存时模拟服务人员和 Apollo 客户端的最佳实践

如何在本地网络上部署带有 apollo 客户端和 apollo-server 后端的 React 应用程序

我如何以及在哪里使用来自 Apollo 客户端的数据?

Rails、Graphql、Apollo 客户端的 Auth Token 无效

带有 apollo-angular 客户端的 Aws-appsync 订阅

如何在 Apollo 客户端的初始化中使用来自 React 的全局数据?