在你的前端应用程序中使用GraphQL,就像在玩一个与使用REST不同的球赛。urql、Apollo Client和Relay等客户端库能够提供与Axios或fetch等REST库不同的功能。
怎么会这样?因为GraphQL是一个有主见的API规范,服务器和客户端都购买了一个模式格式和查询格式。在此基础上,他们可以提供多种高级功能,如缓存数据的工具,基于操作的React Hooks的自动生成,以及乐观的突变。
有时,库可能太有主见,提供了太多的 "魔法"。我使用Apollo客户端已经有一段时间了,对它的缓存和本地状态机制感到很沮丧。
这种 "臃肿",再加上最近看到开源社区的管理不善,终于打破了我的骆驼背。我意识到,我需要到其他地方寻找GraphQL客户端库。
什么是urql?
输入urql,它是一个很好的替代方案。它并不是街区上的新生事物--它从2019年起就存在了--但我刚刚进行了转换,并坚持我的决定。
大多数行话与Apollo客户端相同,这使得从Apollo切换到urql相当直接。urql有大部分相同的功能,但也提供了改进,包括更好的文档,更好的配置默认值,以及对离线模式、文件上传、认证流程和第一方Next.js插件等的第一方支持。
当你把Apollo Client和urql对立起来时,你会开始想为什么Apollo Client当初会如此受欢迎。
再见Apollo客户端
,你好urql
在我写这篇文章的时候,Apollo Client Github仓库的问题数是795。相比之下,urql有16个。"但是问题数与代码质量没有关系!"你可能会这样对我说。这是真的,但它给你的感觉和代码的气味一样--你知道有些东西不对。
深入观察,你可以看到大量的问题未解决,错误需要几个月的时间来修复,而且拉动请求似乎从未被外部贡献者合并过。Apollo似乎没有专注于建立社区想要的伟大的客户端软件包。
这种行为在我看来,Apollo仅仅是将开源用于营销,而不是为了使他们的产品更好。该公司希望你熟悉Apollo客户端,然后购买他们的产品,在我看来这不是真正的开源软件。这是开放核心商业模式的负面因素之一。
我开始在其他地方寻找一个GraphQL客户端,它有一个更快乐和有凝聚力的社区。当一个工具设计得很好,并且具有社区想要的功能时,就会产生较少的问题,对拉动请求的需求就会减少。Formidable是urql背后的机构,他们关心的是以快速和可维护的方式创建应用程序,而不是试图引导用户使用其产品。
为什么使用urql?
对我来说,在与Apollo Client合作了这么久之后,urql是一股新鲜空气。有很多小东西加起来会给开发者带来更好的体验,特别是对新手来说。以下是其中的一些。
urql的文档是全面的
拥有优秀的文档是任何开源库的一个关键特征。没有优秀的文档,社区中就会有更多关于如何使用它以及它在内部如何工作的困惑。我把urql 的详尽文档归结为它的问题数量如此之少的原因。我只花了几个小时就读完了整个文档。
这令人印象深刻,因为它显示了这个库是多么的专注,结构是多么的周密。其中的一些亮点包括这个关于urql如何工作的架构的单页文章,以及这个与其他GraphQL客户端(如Apollo)相比较的表格。
插件和包在urql中有第一方的支持
当我听说urql对离线模式、文件上传、认证和Next.js等额外功能有一流的支持时,真正引起了我的注意。这些都是我一直认为是GraphQL客户端的基本功能,很高兴看到urql对它们有第一方的支持。
例如,urql认证交换包让你只需实现一些方法,就可以在你的客户端内拥有整个认证流程,包括令牌刷新逻辑。你可以在Apollo客户端中实现所有这些东西,但没有官方文档或包。这意味着你要花更多的时间去研究社区解决方案、黑客和代码。
// All the code needed to support offline mode in urql
import { createClient } from 'urql';
import { offlineExchange } from '@urql/exchange-graphcache';
import { makeDefaultStorage } from '@urql/exchange-graphcache/default-storage';
const storage = makeDefaultStorage({
idbName: 'apiCache',
maxAge: 7, // The maximum age of the persisted data in days
});
const cache = offlineExchange({
schema,
storage,
updates: {
/* ... */
},
optimistic: {
/* ... */
},
});
const client = createClient({
url: 'http://localhost:3000/graphql',
exchanges: [cache]
});
同样伟大的是,我不必放弃我在使用Apollo Client时喜欢的东西,比如开发工具和React钩子的生成,因为urql有一个开发工具浏览器扩展和一个graphql-code-generator的插件。
urql中的缓存是简单而有效的
有一个常见的开发者格言,即缓存失效是编程中最难的事情之一。在调试Apollo客户端规范化缓存的许多小时后,我相信了这一点。urql的缓存默认值对新手来说是合理的,并且可以扩展到更高级。
我很欣赏它没有强迫你使用默认的规范化缓存,而是自带了一个文档缓存。这只是通过对查询和其变量进行散列来实现的--它简单而有效
仅仅为了开始使用一个客户端库而学习一个复杂的、完全规范化的缓冲存储的工作方式,似乎是很难的。我觉得Apollo Client只提供规范化的缓存是错误的。
管理规范化缓存的学习曲线很陡峭,而且对许多应用来说是不必要的。urql将其作为一个单独的软件包提供,你可以在以后选择加入,这非常好。我在其他软件包中也看到了这种趋势,比如React Query。
虽然绝大多数用户实际上不需要规范化缓存,甚至不像他们认为的那样能从中受益。
import { ApolloClient, InMemoryCache } from '@apollo/client';
const client = new ApolloClient({
uri: "http://localhost:4000/graphql",
// Normalized cache is required
cache: new InMemoryCache()
});
import { createClient } from "urql";
// Document cache enabled by default
export const client = createClient({
url: "http://localhost:4000/graphql",
});
本地状态在urql中被简化
urql忠实于服务器数据,没有像Apollo Client那样提供管理本地状态的功能。在我看来,这是很好的,因为在React中管理本地状态的完整库越来越不需要。将服务器端的状态和本地状态混合在一起,起初看起来很理想(一个地方可以容纳所有的状态),但当你需要弄清楚哪些数据是新鲜的,哪些是陈旧的,以及何时更新时,会导致问题。
React Context是一个很好的解决方案,适用于你有很多道具钻研的情况,这有时是人们接触本地状态管理库的主要原因。如果你正在寻找一种方法来管理有状态的工作流,我也会推荐XState,有时人们会使用Redux的还原器。
可理解的默认行为与交换器
Exchanges类似于Apollo客户端中的链接,提供了通过拦截请求来扩展客户端功能的方法。与urql不同的是,你可以选择进入甚至是基本的,允许你对客户端的行为有更多的控制和理解。
在开始使用时,客户端没有必要的交换,并使用一个默认的列表。根据我的经验,开始时只有几个交换,随着时间的推移或当我需要它们时,添加更多的交换,使调试更容易。urql显示它在支持许多不同的使用情况方面认真对待可扩展性。
下面是一个在你习惯了 urql 之后可能使用的交换的例子。
import { createClient, dedupExchange, cacheExchange, fetchExchange } from 'urql';
const client = createClient({
url: 'http://localhost:4000/graphql',
exchanges: [
// deduplicates requests if we send the same queries twice
dedupExchange,
// from prior example
cacheExchange,
// responsible for sending our requests to our GraphQL API
fetchExchange,
],
});
uqrl提供了一个Next.js支持插件
Next.js是现在最流行的使用React的方式之一。过去整合Apollo客户端使用Next.js SSR一直是一个巨大的痛苦。每次升级,你都要寻找例子,很可能需要改变它的工作方式。
由于没有来自Apollo的官方插件,你将不得不不断维护这种整合。如前所述,urql有一个针对Next.js的官方插件。这使得它很容易集成。
// Simple React component integrating with Next.js using the plugin
import React from 'react';
import Head from 'next/head';
import { withUrqlClient } from 'next-urql';
import PokemonList from '../components/pokemon_list';
import PokemonTypes from '../components/pokemon_types';
const Root = () => (
<div>
<Head>
<title>Root</title>
<link rel="icon" href="/static/favicon.ico" />
</Head>
<PokemonList />
<PokemonTypes />
</div>
);
export default withUrqlClient(() => ({ url: 'https://graphql-pokemon.now.sh' }))(Root);
总结
当涉及到其统一的社区、优秀的文档、以及第一方插件和缓存系统时,urql比Apollo客户端更有优势。我特别喜欢他们似乎是在与社区一起工作和参与,而不是反对它。
我最近一直在尝试很多GraphQL客户端,看看还有什么可以与Apollo相提并论的,看到urql是如此之好,我感到很新鲜。我预见自己今后会在我所有的GraphQL应用程序中使用它。我希望这能促使你自己试试urql,看看你怎么想。谢谢你的阅读!
The postWhy I (finally) switched to urql from Apollo Clientappeared first onLogRocket Blog.