比较两个流行的 GraphQL API 框架:Relay 和 Apollo。
Relay
官网地址 relay.dev/
优点:
- Relay 是由 Facebook 开发的,与 React 和 GraphQL 有很好的集成。
- Relay 提供了强大的静态类型检查和优化功能,有助于提高应用程序性能。
- Relay 支持自动分页和数据预加载,提高了用户体验。
- Relay 的编译器可以帮助检测和修复潜在的性能问题。
缺点:
- Relay 的学习曲线相对较陡峭,尤其是对于初学者。
- Relay 的文档和社区支持相对较少。
- 不灵活,文件名字 query 名字都是固定的
- 修改graphql后需要手动relay compile
适用场景:
- 大型应用程序,需要更强大的性能优化和类型安全。
- 与 React 和 GraphQL 一起使用的项目。
- 比较复杂的状态管理的大型应用
用法:
- 使用 Relay Compiler 编译 GraphQL 查询。
- 在 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…
优点:
- Apollo 社区非常活跃,有丰富的文档和资源。
- Apollo 支持多种客户端和服务器端框架,具有很好的灵活性。
- Apollo 提供了丰富的开发者工具,便于调试和性能分析。
- Apollo 的学习曲线相对较平缓,适合初学者。
缺点:
- Apollo 的性能优化和类型安全功能相对较弱。
- Apollo 可能需要更多的手动配置和维护。
- 体积比较大。
适用场景:
- 中小型应用程序,需要灵活性和易用性。
- 跨平台项目,需要支持多种客户端和服务器端框架。
用法:
- 使用 Apollo Client 进行 GraphQL 查询和变更操作。
- 在 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())
});