Relay vs Apollo 比较

525 阅读3分钟

比较两个流行的 GraphQL API 框架:Relay 和 Apollo。

Relay

官网地址 relay.dev/

优点:

  1. Relay 是由 Facebook 开发的,与 React 和 GraphQL 有很好的集成。
  2. Relay 提供了强大的静态类型检查和优化功能,有助于提高应用程序性能。
  3. Relay 支持自动分页和数据预加载,提高了用户体验。
  4. Relay 的编译器可以帮助检测和修复潜在的性能问题。

缺点:

  1. Relay 的学习曲线相对较陡峭,尤其是对于初学者。
  2. Relay 的文档和社区支持相对较少。
  3. 不灵活,文件名字 query 名字都是固定的
  4. 修改graphql后需要手动relay compile

适用场景:

  1. 大型应用程序,需要更强大的性能优化和类型安全。
  2. 与 React 和 GraphQL 一起使用的项目。
  3. 比较复杂的状态管理的大型应用

用法:

  1. 使用 Relay Compiler 编译 GraphQL 查询。
  2. 在 React 组件中使用 Relay 提供的高阶组件或钩子。 就以后用realy的都希望能写成钩子 高阶组件太麻烦了

Apollo

官网地址:www.apollographql.com/docs/react/

推荐devtools Apollo Client Devtools www.apollographql.com/docs/react/… 可视化直观看见cache

Apollo Client Devtools chorme 插件下载地址 chrome.google.com/webstore/de…

优点:

  1. Apollo 社区非常活跃,有丰富的文档和资源。
  2. Apollo 支持多种客户端和服务器端框架,具有很好的灵活性。
  3. Apollo 提供了丰富的开发者工具,便于调试和性能分析。
  4. Apollo 的学习曲线相对较平缓,适合初学者。

缺点:

  1. Apollo 的性能优化和类型安全功能相对较弱。
  2. Apollo 可能需要更多的手动配置和维护。
  3. 体积比较大。

适用场景:

  1. 中小型应用程序,需要灵活性和易用性。
  2. 跨平台项目,需要支持多种客户端和服务器端框架。

用法:

  1. 使用 Apollo Client 进行 GraphQL 查询和变更操作。
  2. 在 React 组件中使用 Apollo 提供的高阶组件或钩子。

结论

推荐使用 Apollo。Apollo 与 React 结合使用非常方便,具有良好的灵活性和易用性。此外,Apollo 社区非常活跃,有丰富的文档和资源,对初学者非常友好。虽然 Relay 在性能优化和类型安全方面具有优势,但其学习曲线较陡峭,且社区支持相对较少。因此,对于大多数场景而言,Apollo 是一个更合适的选择。Apollo 比 relay更灵活,relay的query都需要固定的名字,不用index.js导出都找不到组件的入口文件是哪个。其次apollo 改动graphql 也不手动重新执行compiler.

apollo 实践

install

yarn add @apollo/client graphql
// 声明创建 Apollo client
import { onError } from '@apollo/client/link/error';
import { message } from 'antd';
import { ApolloClient, from, HttpLink, InMemoryCache } from '@apollo/client';

const token = localStorage.getItem('token');
const errorLink = onError(({ graphQLErrors, networkError }) => {
  if (graphQLErrors)
    graphQLErrors.forEach(({ message }) => {
      console.log(`[GraphQL error]: Message: ${message}`);
      message.error(`[GraphQL error]: Message: ${message}`);
    });
  if (networkError) {
    console.log(`[Network error]: ${networkError}`);
    message.error(`[Network error]: ${networkError}`);
  }
});
const httpLink = new HttpLink({
  uri: `${process.env.ADMIN_API}/graphql`,
  headers: {
    'token': token,
  },
});

const client = new ApolloClient({
  link: from([errorLink, httpLink]),
  cache: new InMemoryCache(),
  connectToDevTools: true,
});

export default client;

// 在入口引入ApolloProvider
<ApolloProvider client={client}>...</ApolloProvider>

// 在页面使用
const someQuery = gql`
  query ...
`;

const mutation = gql`
  mutation ... 
`;

const MyComponent = () => {
  // 使用hook 去query mutation
  const [pageInfo, setPageInfo] = useState({ pn: 1, ps: 20 });
  const {
    loading,
    data
    refetch,
  } = useQuery(someQuery, {
    variables: { first: pageInfo.pn, after: `${pageInfo.ps}` },
  });
  const [doMutation] = useMutation(mutation);
  const columns = [
    {
      title: 'name',
      dataIndex: 'name',
     
    },
    ...
    {
      title: 'Actions',
      key: 'action',
      render: (record) => (
        <>
          <a
            onClick={async () => {
              await doMutation({
                variables: { id:record.id },
              });
              refetch();
            }}
          >
            Do
          </a>
        </>
      ),
    },
  ];
  return (
    <Table
          columns={columns}
          dataSource={data?.data || []}
          loading={loading}
          pagination={{
            current: pageInfo.pn,
            pageSize: pageInfo.ps,
            total: data?.totalCount,
            onChange: (page) => {
              setPageInfo({ ...pageInfo, pn: page });
            },
          }}
          rowKey={(record) => record.node.id}
        />
  )
}

Relay 实践

relay v11版本之前不支持 hook install

yarn add  react-relay
yarn add --dev relay-compiler
yarn add --dev babel-plugin-relay graphql
// 组件名字要是 文件夹的名字+QueryRender
const MyQueryRender = (data) => (
  <QueryRenderer
    environment={modernEnvironment}
    query={graphql`
      query ...
    `}
    render={({ error, props, retry: tabRefetch }) => {
      if (error) {
        return <ErrorState />;
      }
      if (props) {
        return (
          <MyComponent query={props} {...data} tabRefetch={tabRefetch} />
        );
      }
      return <Loading />;
    }}
    variables={{
     ....
    }}
  />
);

// modernEnvironment
const headerLink = new ApolloLink((operation, forward) => {
  operation.setContext({
    headers: {
      'token': 'token',
    },
  });
  return forward(operation);
});


const httpLink = new HttpLink({
  uri: `${API_URL}/graphql`,
  credentials: 'include',
  fetch,
});

const composedLink = from([
  headerLink,
  httpLink,
]);


export const modernEnvironment = new Environment({
  network: Network.create((operation, variables) => makePromise(execute(composedLink, {
    query: parse(operation.text),
    variables,
  }))),
  store: new Store(new RecordSource())
});