为什么我(最终)从Apollo客户端转到了urql?

798 阅读8分钟

在你的前端应用程序中使用GraphQL,就像在玩一个与使用REST不同的球赛。urqlApollo ClientRelay等客户端库能够提供与Axiosfetch等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.