前言和动机

在Okcupid188bet金宝搏官网,我们非常大的粉丝使用GraphQL.。何时提取在我们的任何客户平台上的数据时,查询语言提供授予我们在每种情况下所需的数据的灵活性的抽象。

在一天结束时,GraphQL真的就是:抽象。突变,查询和订阅类型抽象地模拟了我们与任何数据交互的基本方式。该模式在某些数据源和目的地之间作为合同,它定义了可以查询的数据以及它应该如何查询。我们的GraphQL服务器实例在大多数情况下将解决传入查询概述的数据,但该数据的目的地(在我们的情况下,让我们说这是一个移动应用程序或作为客户端的Web应用程序,并不真正需要要了解有关数据的来源或分辨率策略。

这真的很好,因为这意味着数据可以来自GraphQL服务器可以访问的任何地方。也许我们希望使用文件系统中的某些东西来解决我们的数据,或者可能是本地数据库,或者可能是远程数据库。也许我们可以通过RPC或休息或任何协议将其他其他服务器调用,或任何协议。也许我们的数据目前在某处内存,这也很好!我们的数据源的漠不关心是允许此模型的允许,以及数据图的体系结构如此可扩展(Mandi Wise有一个展示这一点的很棒的视频涵盖联邦图的概念)。

无论数据的来源如何,客户端实施都没有真正需要更改,这是至关重要的了解使用GraphQL获取本地管理的概念很重要。想象一下你的应用程序的当地国家:它真的只是另一个数据来源,毕竟不是吗?所以,那么问题是,这是什么阻止我们利用此查询语言范例提供给我们的抽象来管理此数据?

这个怎么运作

好吧,答案是什么。如果您在应用程序中的某个位置存储状态,则可以理解地刚刚将GraphQL查询解析为具有该状态数据,如果需要。实施从阿波罗(我们看来,我们尝试使用GraphQL指令的所有事物GQL的事实上提供者,以表示应以这种方式解决哪些给定查询:@客户

有关那个看起来的示例,让我们想象我们希望拥有用户可以彼此发送消息的应用程序。要获取有关用户有邮件的信息,我们可能会对我们的服务器进行查询。但是,也许我们想知道用户目前是否有一个给定的对话的任何消息窗口。服务器实际上并不关心每次对话的这段信息,但我们的前端应用程序会关心它,因此我们可能会发现我们可能会选择在客户状态下存储类似的东西。假设是这种情况,然后可以为该数据构建合理的查询,包括所包括的本地状态,如下所示:

查询getAllMessages($ UserID:ID!){用户(ID:$ UserID){name profilepic消息{id eperadent {name profilepic} isopen @client}}}

在这个例子中,我们的开了用户邮件的标志可以存储在我们的客户状态中,因为它并不大部分问题。但是,可以从我们的服务器获取其余的数据,并且没有其他需要更改。能够将我们的数据源(客户端与服务器)与单个查询混合是一个非常强大的想法,可以导致一些令人难以置信的灵活的单个查询。

因为战略解决这个问题@客户指令是一个遍历,这意味着该指令可以在我们的数据中递归地应用于父母和邻居关系,允许我们实现数据图的相同体验,除了我们的客户状态。我们的客户状态可以直接访问其父级(非客户端状态)结构,并且它可以像Boolean标志等标量字段一样简单,甚至是更深入地相关和结构的数据。

客户状态不需要与某些服务器数据相关!它真的可以是我们可能想要存储为客户状态的任何数据,例如用户当前的主题偏好的当前设置,完全是前端关注,但仍然存在一些状态。虽然暗模式是如此,但是,对了吗?

那么,这样的工作如何?嗯,在引擎盖下,我们需要向我们的新配置添加一些新配置apolroclient.实例,以便它具有解决客户端查询的策略。为此,我们通过向我们的客户端添加一些新的解压器来显式拼写这一点,就像我们写入我们的服务器实例上的解析查询一样。该apolroclient.实例可以在初始化后以及使用ad-hoc时添加resolversclient.addresolver(Somenewresolvertoadd)。Apollo定义了处理如此如此的函数签名,如果您合作,它应该看起来非常熟悉Apollo-Server.在过去:

键入resolverfn =(父级:ANY,ARGS:ANY,{Cache}:{Cache:Apolrocache })=>任何;

忽略父节点和参数参数,我们看到我们破坏了对象的属性,调用缓存,就像我们破坏a数据源物业在Apollo-Server.与此功能类型并行。这是因为在这种情况下,我们的缓存已经承担了客户端世界的DataSource的责任。让我们看看在此上下文中的客户端解析器安装程序将是什么样的:

const defaultresolver = {查询:{user:{messages:{isopen :(父,args,{cache})=> {//引用缓存以获取数据返回cache.readquery({查询:message_is_open_query,变量:{message:parent.id,}});},},},},};

之后,我们还希望使用一些初始状态提供缓存,因此我们的第一个缓存读取将解决,因此我们也希望在初始化时将其馈送到我们的缓存中并将其馈送到我们的缓存。

//定义初始客户端状态const defaultstate = {user:{messages:{issopen:false}} const client client = new apolloclient({//其他apollo config,例如链接//和缓存定义解析器:defaultresolver,});//使用初始状态Client.WriteData({查询:Message_Is_Open_Query,Data:{DefaultState,}})将缓存括起来

在此示例中,我们只是为给定消息设置默认状态

Apollo的直觉是不仅提供此指令和解决客户端在客户端的查询的选项,而且可以提供最多的客户端缓存apolroclient.无论如何都定义了实例(通常用于将其查询的响应存储在服务器上的查询)作为存储此客户端状态的位置。这有很多意义,我们有一个本地商店(我们的apolroclient.'s缓存),我们有办法与该商店互动(使用GQL)......这实际上是什么样的解决方案(我肯定需要在这一点上没有介绍)或莫克斯为我们提供对吗?

嗯,是。它也完全良好!当我们探索这一点时,我们注意到一些最终导致我们的一些事情导致我们决定不依赖于Apollo进行国家管理。

我们遇到的问题

那么我们为什么决定实施这一点?嗯,决定背后的推理肯定是我们的情景,也许不会对他人同样重要,但确实包含一些我认为会考虑任何人沿着阿波罗路径的考虑会重视。

开发人员开销和学习曲线

虽然这是一个舞台管理解决方案,但它带来了一些新的开销。一个人现在必须为他们的客户端状态编写解析器,虽然是这种情况任何国家管理选项,它看起来并不像它看起来那么简单。

要真正地编写这些解析器,您将希望在直接与缓存一起工作时有一些体验/能力。第3.0版,阿波罗/客户有一些人对缓存如何处理归一化和非归一化数据的相当大幅度更新。了解缓存如何使用IDS和__typename.s,决定是否合并或替换数据,并学习如何使用此范例的课程标准。Sidenote,Khalil @ Apollo最近发表了一个令人难以置信的博客文章深入了解Apollo缓存并了解缓存归一化。这为我们提供了两个备受缓存数据的选项之一:确保每个查询都请求唯一识别我们所要求的数据的唯一识别字段(这并不易于请求我们想要的任何领域的目的?)或写作明确Typepolicies.要告诉我们的缓存如何标准化我们的数据。从撰写客户端库的人的角度来看,必须为众多不同的使用情况正常工作,我理解这样的解决方案的动机。但是,这不是客户端状态通过像Redux这样的解决方案的问题。

对比这种范例对抗类似的东西React的上下文API与...的串联Userducer Hook.甚至是Redux架构,似乎是Apollo解决方案更多的是从开发人员角度来理解和管理。但是,对于这个权衡,我们确实获得了思考和互动的能力我们所有的应用程序的数据以同样的方式,这无疑是一个令人敬畏的好处。但是这值得吗?

一些新的东西,借来的东西?

嗯,我们已经在OKC的旧货币中使用了Redux,即使是回流,也在一些旧代码的示例中。在我们的应用中添加一个新的选项,它将真正导致混乱的混乱,这对船上的人无疑是复杂的,这是一个首先将头包裹在一起。就个人而言,我觉得开发者的经验和可维护性应该是巨大定义任何架构或框架决定的因素。

“Redux已经死了”参数已经多次提出,以及与之相关的传统成本可以轻松地认为样板和包装组件不是非常可扩展的,因为一个人必须在到达任何地方之前对少数文件进行更改。尽管如此,它肯定已经成熟了多年。如果做得好,它绝对可以使用微风,它清楚地确实有一些留力(更不用说,与Redux钩子一起工作实际上非常好)。更不用说,剥夺我们现有的架构需要数月,并添加另一个范例来学习和遵循现有的国家管理范例,当DEVS编写代码时会导致更多的上下文切换,并且可能只是混淆并更多地卷起它们更多。

最重要的是,在一个人的处理中也有无数资源使用和了解Redux(或任何成熟的OSS,或者对于此事项),我当然在研究阿波罗客户端州管理时肯定有问题;Apollo方法中的文档,视频和文章也许是因为与Redux这样的东西相比,它的生活更加模糊或早期。此外,更成熟的解决方案可能会在为开发人员合作的稳定API的条款中可能提供更多价值,而在这方面则更年轻人可能更加动荡(这一切都是非常态度,但我将承认我将承认)。

但是,违背冗余的常见争论必须“改变如此多的文件”和“过度复杂化”并没有真正被Apollo的解决方案纠正。我们仍然希望明智地哄骗腐败师和初始状态的定义,并且在这样做的事情之后,我觉得我只是写了一些redux,这对我来说感到非常讽刺。除了与商店一起工作,似乎直接与缓存直接工作似乎不太复杂。

Apollo还有一个Dev Tools产品,我真的很喜欢使用并发现是有用的,但在忍受像Redux的并行之类的东西时,它感觉太不成熟了。有时,它不想发布。它没有提供有趣的功能,如时间旅行,我对使用它的一件事是为客户端的内省使用它,所以需要定义typedefs.在这一点apolroclient.实例(这基本上是为我们的客户端状态创建架构的过程,这实际上只是担心和管理的另一个整个事情,但我承认,或许可以在这种情况下真正发光的打字或代码。。在我使用的其他库中,没有必要多次定义客户端状态的形状,如果有的话,它开始看起来像一些开始累积的阿波孔侧的样板。

其他选择

我们也一直在尝试反应的上下文API,并且可能希望将来考虑其他一些选择。但是,对我们选择对我们捆绑尺寸的影响的思考也非常重要。Contuct / Apollo可以完全删除对状态管理依赖的需求吗?对于一些更简单的应用程序,我认为那里的例子被证明的上下文已经足够了。同样,Apollo的解决方案的一些例子也足够了!还有Mobx,Facebook的新开源提供畏缩甚至使用状态机建模客户端状态XSTATE.

我们在哪里网?

很难承认阿波罗所做的令人难以置信的工作。Apollo和GraphQL已经完成了清理我们的API和客户的一般网络层的奇迹。然而,追求其作为国家管理层的选择,对我们的代码库具有重大影响,目前我们只是没有觉得这一论点足够引人注目,但由于阿波罗的客户端管理和当前我们的客户设置- 邦德州。在我的意见中,在具有很多现有架构中实施具有很多现有架构的码级内容的返回投资需要相对较高。我只是不确定与阿波罗的投资回报足够高,在这种情况下足够高。

虽然探索阿波罗提供了一些新的洞察力,但是,我们希望能够最终能够更好地得出更好的结论,以及我们应该依靠解决问题,以及我们如何考虑方法,架构和我们决定滚动的任何图书馆,框架或工具的权衡。在此之前,我们将继续保持我们的眼睛如何掌握Apollo的客户状态解决方案的发展。