跨界面数据同步更新:基于抽象数据源的前端数据管理模式

广告位招租
扫码页面底部二维码联系
在一个应用中,同一个数据源被用在不同场景【作者:唐霜】【访问 www.tangshuang.net 获取更多精彩内容】下,会被反复请求,数据更新后不太容易同步【访问 www.tangshuang.net 获取更多精彩内容】【访问 www.tangshuang.net 获取更多精彩内容】更新关联数据源,我们通过抽象数据源,提出【转载请注明来源】【未经授权禁止转载】了一套可充分管理数据的系统化方案。
【本文首发于唐霜的博客】【原创不易,请尊重版权】【本文受版权保护】未经授权,禁止复制转载。

前端开发中,从后端提供的接口请求数据和提【原创内容,转载请注明出处】【关注微信公众号:wwwtangshuangnet】交数据是非常基础的工作,但在我们很多业务本文作者:唐霜,转载请注明出处。著作权归作者所有,禁止商业用途转载。中,我们会发现跨界面的数据同步更新成为非原创内容,盗版必究。【作者:唐霜】常棘手的问题。我认为,由于很大一部分前端【关注微信公众号:wwwtangshuangnet】转载请注明出处:www.tangshuang.net对数据管理的方式非常粗糙,给后续很多开发【版权所有】唐霜 www.tangshuang.net【原创内容,转载请注明出处】工作和产品体验都带来了问题,要解决这一问【版权所有】唐霜 www.tangshuang.net【转载请注明来源】题,我们需要从更深的层面去思考“前端数据【本文受版权保护】【作者:唐霜】管理”的设计问题。本文将结合我们腾讯投资【原创不易,请尊重版权】【本文首发于唐霜的博客】项目组的实战经验,聊一聊我对这个问题的思【关注微信公众号:wwwtangshuangnet】本文作者:唐霜,转载请注明出处。考和实现方式。

原创内容,盗版必究。【转载请注明来源】【版权所有】唐霜 www.tangshuang.net

背景【转载请注明来源】

【作者:唐霜】【访问 www.tangshuang.net 获取更多精彩内容】【访问 www.tangshuang.net 获取更多精彩内容】【未经授权禁止转载】【版权所有,侵权必究】

前端开发逐渐成熟,但于此同时也存在诸多问【访问 www.tangshuang.net 获取更多精彩内容】原创内容,盗版必究。题。具体到本文的场景下,成熟是指我们的开【版权所有,侵权必究】【原创内容,转载请注明出处】发模块化、逻辑分离化,有很好的分层趋势,转载请注明出处:www.tangshuang.net【原创不易,请尊重版权】而诸多问题是指由于前端编程本身的一些局限【本文首发于唐霜的博客】【本文受版权保护】以及生态的不成熟,在分层趋势下无法优秀的【版权所有】唐霜 www.tangshuang.net原创内容,盗版必究。处理跨层级的通信传递问题。

著作权归作者所有,禁止商业用途转载。【版权所有】唐霜 www.tangshuang.net本文版权归作者所有,未经授权不得转载。【关注微信公众号:wwwtangshuangnet】本文版权归作者所有,未经授权不得转载。

我们以tapd的一个例子为例来看看。当我著作权归作者所有,禁止商业用途转载。【原创内容,转载请注明出处】们在任务列表中更新了某个单的状态时,可以未经授权,禁止复制转载。未经授权,禁止复制转载。通过一个快捷弹窗进行更新,更新后新状态会【原创不易,请尊重版权】【原创内容,转载请注明出处】被同步到列表中,这个体验非常好:

本文版权归作者所有,未经授权不得转载。【访问 www.tangshuang.net 获取更多精彩内容】转载请注明出处:www.tangshuang.net【转载请注明来源】本文版权归作者所有,未经授权不得转载。

 本文作者:唐霜,转载请注明出处。

转载请注明出处:www.tangshuang.net【版权所有】唐霜 www.tangshuang.net【本文首发于唐霜的博客】【原创内容,转载请注明出处】【转载请注明来源】

但是,当我们打开单的详情,在详情界面更新【本文首发于唐霜的博客】未经授权,禁止复制转载。状态时,在点击关闭之后,状态却没有同步到本文版权归作者所有,未经授权不得转载。【版权所有】唐霜 www.tangshuang.net列表,这个体验就不那么如人意:

【未经授权禁止转载】【原创内容,转载请注明出处】本文作者:唐霜,转载请注明出处。【转载请注明来源】本文作者:唐霜,转载请注明出处。

 【版权所有】唐霜 www.tangshuang.net

【原创内容,转载请注明出处】【版权所有】唐霜 www.tangshuang.net未经授权,禁止复制转载。未经授权,禁止复制转载。著作权归作者所有,禁止商业用途转载。

当然,对于tapd团队来讲,他们可以有办未经授权,禁止复制转载。原创内容,盗版必究。法解决这个问题,甚至这个情况可能是他们的未经授权,禁止复制转载。转载请注明出处:www.tangshuang.net一个bug,漏掉了一段代码。但是,我们不【原创不易,请尊重版权】原创内容,盗版必究。针对tapd的这个情况,我们要思考的是,本文作者:唐霜,转载请注明出处。【本文受版权保护】当我们在列表和详情两个页面进行操作时,如未经授权,禁止复制转载。【原创内容,转载请注明出处】何在这两个界面之间同步数据的更新呢?

【原创不易,请尊重版权】未经授权,禁止复制转载。转载请注明出处:www.tangshuang.net【本文受版权保护】【版权所有,侵权必究】

我们经常在一个页面,甚至跨层级的组件之间转载请注明出处:www.tangshuang.net【原创不易,请尊重版权】有类似的操作。在一个页面的某个角落里面进【版权所有,侵权必究】【未经授权禁止转载】行了某个编辑,提交之后,要同步更新页面大【版权所有】唐霜 www.tangshuang.net本文版权归作者所有,未经授权不得转载。老远的另外一个角落的数据,因为它们是关联未经授权,禁止复制转载。【原创不易,请尊重版权】的,可能其中一个是依赖另外一个进行计算得转载请注明出处:www.tangshuang.net【关注微信公众号:wwwtangshuangnet】到的数据,当被依赖的数据发送变化时,依赖本文版权归作者所有,未经授权不得转载。著作权归作者所有,禁止商业用途转载。者应该及时更新,否则界面上就会出现数据不【原创不易,请尊重版权】著作权归作者所有,禁止商业用途转载。一致的问题,是明显的业务错误。

【访问 www.tangshuang.net 获取更多精彩内容】本文版权归作者所有,未经授权不得转载。【原创内容,转载请注明出处】

但是我们在开发时,往往这两个地方是分两个著作权归作者所有,禁止商业用途转载。【作者:唐霜】组件开发的,而且很有可能这两个组件之间跨著作权归作者所有,禁止商业用途转载。原创内容,盗版必究。了十万八千里的层级,想要轻易通信没那么容【版权所有,侵权必究】未经授权,禁止复制转载。易。面对这样的场景,我们应该如何去思考,未经授权,禁止复制转载。【原创不易,请尊重版权】如何去找到一种合理的方式呢?

本文版权归作者所有,未经授权不得转载。【原创不易,请尊重版权】转载请注明出处:www.tangshuang.net【转载请注明来源】原创内容,盗版必究。

备选方案【关注微信公众号:wwwtangshuangnet】

【作者:唐霜】【版权所有,侵权必究】本文版权归作者所有,未经授权不得转载。【版权所有,侵权必究】【作者:唐霜】

愚公移山法【版权所有,侵权必究】

【关注微信公众号:wwwtangshuangnet】【原创内容,转载请注明出处】原创内容,盗版必究。原创内容,盗版必究。

例如上面的两个组件我们称为A和B,其中B【本文首发于唐霜的博客】【原创内容,转载请注明出处】组件内可能发生更新,更新后需要同步更新A【版权所有,侵权必究】【转载请注明来源】组件。我们通过在A上暴露出一个方法,通过【本文首发于唐霜的博客】【关注微信公众号:wwwtangshuangnet】组件网络,层层传递,把这个方法传入到B中【未经授权禁止转载】【转载请注明来源】,当B完成更新后,调用这个方法,这个方法本文作者:唐霜,转载请注明出处。本文作者:唐霜,转载请注明出处。会帮助A重新请求数据,并完成界面更新。

本文版权归作者所有,未经授权不得转载。本文作者:唐霜,转载请注明出处。【关注微信公众号:wwwtangshuangnet】

全局状态法原创内容,盗版必究。

本文作者:唐霜,转载请注明出处。著作权归作者所有,禁止商业用途转载。【版权所有】唐霜 www.tangshuang.net

我们建立了一个全局状态,该状态同时被A和转载请注明出处:www.tangshuang.net【版权所有,侵权必究】B使用,当B完成更新后,我们重新请求数据【访问 www.tangshuang.net 获取更多精彩内容】转载请注明出处:www.tangshuang.net,把新的数据写入到全局状态,引起两个组件本文版权归作者所有,未经授权不得转载。【未经授权禁止转载】的同时更新。但是有一个问题,我们是否需要本文作者:唐霜,转载请注明出处。著作权归作者所有,禁止商业用途转载。基于不同的业务对象ID存储无数个类似的全【访问 www.tangshuang.net 获取更多精彩内容】原创内容,盗版必究。局状态?

著作权归作者所有,禁止商业用途转载。【版权所有】唐霜 www.tangshuang.net【访问 www.tangshuang.net 获取更多精彩内容】【访问 www.tangshuang.net 获取更多精彩内容】【作者:唐霜】

事件通知法【关注微信公众号:wwwtangshuangnet】

【未经授权禁止转载】【原创内容,转载请注明出处】【转载请注明来源】【版权所有】唐霜 www.tangshuang.net【转载请注明来源】

使用类似EventBus一类的事件管理器本文作者:唐霜,转载请注明出处。原创内容,盗版必究。,在B完成更新后,广播一个事件,A监听该转载请注明出处:www.tangshuang.net【原创内容,转载请注明出处】事件,在监听回调中重新请求数据并更新界面【版权所有】唐霜 www.tangshuang.net【原创内容,转载请注明出处】。问题在于,我们怎么确保庞大的系统中,事原创内容,盗版必究。本文作者:唐霜,转载请注明出处。件名是唯一的?

【原创不易,请尊重版权】【本文首发于唐霜的博客】【本文受版权保护】转载请注明出处:www.tangshuang.net

问题的本质本文作者:唐霜,转载请注明出处。

【访问 www.tangshuang.net 获取更多精彩内容】【转载请注明来源】本文作者:唐霜,转载请注明出处。本文作者:唐霜,转载请注明出处。

我们经常讲,界面是状态反映。在这中间,我【原创不易,请尊重版权】【原创内容,转载请注明出处】们没有提到数据是什么。我们从后端拉取的数本文版权归作者所有,未经授权不得转载。【版权所有】唐霜 www.tangshuang.net据是什么呢?是状态?是其他?这是问题其一原创内容,盗版必究。本文作者:唐霜,转载请注明出处。。问题之二是,我们现有的编程技巧中,把数【版权所有,侵权必究】【访问 www.tangshuang.net 获取更多精彩内容】据请求回来作为渲染界面的依据,这种处理方本文版权归作者所有,未经授权不得转载。【访问 www.tangshuang.net 获取更多精彩内容】式直接把数据的来源与界面进行了绑定,以至【未经授权禁止转载】【版权所有】唐霜 www.tangshuang.net于对数据的过度依赖致使UI编程无法以纯粹【本文受版权保护】【转载请注明来源】的方式表达界面本身的呈现。另一方面,数据未经授权,禁止复制转载。【本文受版权保护】源和界面的点对点关系,忽略了数据本身的意本文版权归作者所有,未经授权不得转载。【转载请注明来源】义,破坏了数据本身的联动性,使得这种联动【版权所有,侵权必究】【转载请注明来源】必须依赖于UI编程中的某种触发,而非其自【作者:唐霜】本文版权归作者所有,未经授权不得转载。己实现闭合回路。数据本身的生命形态被打破【访问 www.tangshuang.net 获取更多精彩内容】【转载请注明来源】,UI编程又被数据锁死,这是导致本文所指著作权归作者所有,禁止商业用途转载。未经授权,禁止复制转载。问题的关键根源。

【原创内容,转载请注明出处】原创内容,盗版必究。原创内容,盗版必究。【本文受版权保护】转载请注明出处:www.tangshuang.net

一种数据源管理服务层设计【本文受版权保护】

著作权归作者所有,禁止商业用途转载。【原创不易,请尊重版权】本文作者:唐霜,转载请注明出处。转载请注明出处:www.tangshuang.net

万事不决,分离一层。原创内容,盗版必究。

【未经授权禁止转载】【访问 www.tangshuang.net 获取更多精彩内容】【原创不易,请尊重版权】转载请注明出处:www.tangshuang.net【访问 www.tangshuang.net 获取更多精彩内容】

我认为,在我们纯粹的UI编程和数据之间缺【原创不易,请尊重版权】【本文首发于唐霜的博客】少一层隔离,所以界面和数据源之间是直接点未经授权,禁止复制转载。【版权所有】唐霜 www.tangshuang.net对点绑定的,就像电话两头一样,只能两个人【版权所有,侵权必究】【本文首发于唐霜的博客】参与。而我们所面临的问题需要我们像水管一【版权所有】唐霜 www.tangshuang.net【版权所有,侵权必究】样,水源来了,你可以洗手盆出水,也可以花【本文首发于唐霜的博客】本文作者:唐霜,转载请注明出处。洒出水,都是用水,但用法不同,是一对多分【原创内容,转载请注明出处】未经授权,禁止复制转载。发式参与。

【本文首发于唐霜的博客】本文作者:唐霜,转载请注明出处。著作权归作者所有,禁止商业用途转载。【原创内容,转载请注明出处】【访问 www.tangshuang.net 获取更多精彩内容】

在数据源和具体界面之间,并非一对一的关系原创内容,盗版必究。转载请注明出处:www.tangshuang.net,而且很多情况下,一个界面不一定只依赖一【版权所有】唐霜 www.tangshuang.net【本文首发于唐霜的博客】个数据源。因此,我们应该可以有这样一种形未经授权,禁止复制转载。【版权所有,侵权必究】态:界面只管取数据来用,数据从哪里来我并不关本文版权归作者所有,未经授权不得转载。转载请注明出处:www.tangshuang.net心;于此同时,数据变了,我界面跟着变。通过这种隔离,让数据本身的生命由数据层自【转载请注明来源】原创内容,盗版必究。己掌控,让UI界面编程不在乎后端接口的绑【原创内容,转载请注明出处】【关注微信公众号:wwwtangshuangnet】架,从而做到两端的分离。

未经授权,禁止复制转载。【本文受版权保护】【版权所有】唐霜 www.tangshuang.net著作权归作者所有,禁止商业用途转载。【关注微信公众号:wwwtangshuangnet】

我们举个例子来感受一下:【本文首发于唐霜的博客】

转载请注明出处:www.tangshuang.net原创内容,盗版必究。【版权所有】唐霜 www.tangshuang.net【版权所有】唐霜 www.tangshuang.net

我现在有了一个数据,它的格式为 { na著作权归作者所有,禁止商业用途转载。【版权所有】唐霜 www.tangshuang.netme: string, count: n【原创内容,转载请注明出处】【关注微信公众号:wwwtangshuangnet】umber },那么在任何界面中,我都可【版权所有】唐霜 www.tangshuang.net著作权归作者所有,禁止商业用途转载。以将它取出来用,至于这个数据从哪里来,此【作者:唐霜】【未经授权禁止转载】刻我们并不关心。我们的组件需要用到这个数【本文首发于唐霜的博客】【未经授权禁止转载】据,因此,我们把它读取出来。当这个数据发【版权所有,侵权必究】【本文首发于唐霜的博客】生变化(比如提交了更新,或者websoc【原创不易,请尊重版权】本文版权归作者所有,未经授权不得转载。ket推送了新数据)的时候,我们的界面随本文版权归作者所有,未经授权不得转载。【未经授权禁止转载】之变化。我们的组件只是像某个东西索取了这【本文首发于唐霜的博客】【转载请注明来源】份数据,并且基于typescript,我【未经授权禁止转载】未经授权,禁止复制转载。们知道这份数据的格式和类型,至于这份数据【本文受版权保护】原创内容,盗版必究。是怎么来的,是从后端接口拉取的还是从缓存【原创不易,请尊重版权】原创内容,盗版必究。读取的,是由单一接口还是多个接口组合而来著作权归作者所有,禁止商业用途转载。原创内容,盗版必究。的,对于我组件而言,我不关心,我只关心我【版权所有,侵权必究】未经授权,禁止复制转载。能拿到这份数据。

著作权归作者所有,禁止商业用途转载。著作权归作者所有,禁止商业用途转载。【版权所有】唐霜 www.tangshuang.net
function SomeComponent() {
  const [data] = useDataSource(SomeDataSource)
  const { name, count } = data

  return (
    <div>{name}: {count}</div>
  )
}

那么这个数据是怎么来的呢?当然是从我们的【原创不易,请尊重版权】【版权所有】唐霜 www.tangshuang.net后端接口来的了。

【作者:唐霜】【作者:唐霜】本文作者:唐霜,转载请注明出处。
const SomeDataSource = source(async () => {
  const data = await fetch('...')
  return data
}, { name: '', count: 0 })

这个数据可能发生变化,什么时候发生变化呢【版权所有,侵权必究】本文版权归作者所有,未经授权不得转载。?当然是某次更新操作之后。

【关注微信公众号:wwwtangshuangnet】【未经授权禁止转载】【访问 www.tangshuang.net 获取更多精彩内容】
await postData(data)
await renew(SomeDataSource)

当执行 `renew` 时,SomeDa未经授权,禁止复制转载。本文作者:唐霜,转载请注明出处。taSource 中的请求函数会被再次执【未经授权禁止转载】本文版权归作者所有,未经授权不得转载。行,新拿到的数据将作为 SomeData著作权归作者所有,禁止商业用途转载。【作者:唐霜】Source 的新数据,由于这一变更会带原创内容,盗版必究。【版权所有,侵权必究】来 useDataSource 内部实现本文版权归作者所有,未经授权不得转载。本文版权归作者所有,未经授权不得转载。的状态变更,从而引起 SomeCompo【版权所有】唐霜 www.tangshuang.net未经授权,禁止复制转载。nent 的重新渲染。

著作权归作者所有,禁止商业用途转载。【访问 www.tangshuang.net 获取更多精彩内容】【访问 www.tangshuang.net 获取更多精彩内容】【访问 www.tangshuang.net 获取更多精彩内容】【版权所有】唐霜 www.tangshuang.net

假如,我们在另外一个组件中也使用了该数据转载请注明出处:www.tangshuang.net未经授权,禁止复制转载。源:

未经授权,禁止复制转载。【本文受版权保护】【版权所有,侵权必究】
function Some2Component() {
  const [data] = useDataSource(SomeDataSource)
  ...
}

那么,当 SomeDataSource 【作者:唐霜】【关注微信公众号:wwwtangshuangnet】中对应数据的变化,就回同时带来 Some转载请注明出处:www.tangshuang.net未经授权,禁止复制转载。2Component 的重新渲染。

【版权所有,侵权必究】【原创不易,请尊重版权】本文版权归作者所有,未经授权不得转载。【未经授权禁止转载】

你可能会说,这有什么,感觉完全没什么难度原创内容,盗版必究。【原创不易,请尊重版权】。但是一旦你开始按照这种思路去设计和架构未经授权,禁止复制转载。【访问 www.tangshuang.net 获取更多精彩内容】你的代码,你就会发现,数据和组件开始分离【未经授权禁止转载】【未经授权禁止转载】,组件专注于使用数据,而数据有了自己的生转载请注明出处:www.tangshuang.net原创内容,盗版必究。命形态。

【转载请注明来源】著作权归作者所有,禁止商业用途转载。【本文首发于唐霜的博客】【未经授权禁止转载】

数据源管理服务层未经授权,禁止复制转载。

著作权归作者所有,禁止商业用途转载。未经授权,禁止复制转载。转载请注明出处:www.tangshuang.net

在腾讯投资项目中,我们建立了一层服务层,【本文受版权保护】【本文首发于唐霜的博客】专门用于管理对应的数据源,称之为数据源管【版权所有,侵权必究】【原创不易,请尊重版权】理服务层。数据源管理和数据请求是两个层面【访问 www.tangshuang.net 获取更多精彩内容】【作者:唐霜】的事物,数据请求负责从服务端拉取数据,二【未经授权禁止转载】原创内容,盗版必究。数据源管理负责管理这些数据。“数据源”是著作权归作者所有,禁止商业用途转载。转载请注明出处:www.tangshuang.net一个抽象,例如我们有一个叫做“项目详情”【版权所有,侵权必究】本文版权归作者所有,未经授权不得转载。的数据源,但它并不代表一个具体的接口数据【本文首发于唐霜的博客】本文版权归作者所有,未经授权不得转载。,而是代表项目这个概念的数据,抽象和具体转载请注明出处:www.tangshuang.net【访问 www.tangshuang.net 获取更多精彩内容】的关系,类似interface和impl原创内容,盗版必究。本文作者:唐霜,转载请注明出处。ements的关系。这个“项目详情”数据原创内容,盗版必究。本文作者:唐霜,转载请注明出处。源,可能对应无数个具体项目的数据,但是它【版权所有,侵权必究】【本文首发于唐霜的博客】是一个源,它内部组织着不同项目数据的存储未经授权,禁止复制转载。【版权所有】唐霜 www.tangshuang.net,所以它是一个管理工具。

【原创内容,转载请注明出处】【原创不易,请尊重版权】本文版权归作者所有,未经授权不得转载。【关注微信公众号:wwwtangshuangnet】

数据源对象维持着数据,这些数据在组件中被未经授权,禁止复制转载。【关注微信公众号:wwwtangshuangnet】读取。但是,在没有组件之前,我们就可以对转载请注明出处:www.tangshuang.net【转载请注明来源】数据源进行编程。我们以某些具体的需求为目转载请注明出处:www.tangshuang.net【原创内容,转载请注明出处】标,建立数据源,数据源的目标是像下游提供【本文首发于唐霜的博客】【未经授权禁止转载】数据,所以它只关心输出的数据是准确的,而【作者:唐霜】转载请注明出处:www.tangshuang.net不关心数据从哪里来。数据源定义拉取数据时【本文受版权保护】【原创内容,转载请注明出处】,我们可以从两个接口挑选对应数据组合起来本文版权归作者所有,未经授权不得转载。本文版权归作者所有,未经授权不得转载。作为数据源对应的数据。例如一个具体的项目【本文受版权保护】著作权归作者所有,禁止商业用途转载。详情,它需要包含项目的基本信息,也需要包【原创不易,请尊重版权】【本文首发于唐霜的博客】含这个项目内的交易信息,但是对于后端而言著作权归作者所有,禁止商业用途转载。【原创内容,转载请注明出处】,这两个信息是分开的,由两个接口提供,因本文版权归作者所有,未经授权不得转载。【原创不易,请尊重版权】此,我们在建这个数据源时,我们从这两个接【关注微信公众号:wwwtangshuangnet】【原创内容,转载请注明出处】口拿到数据,并合并为一个数据,存储在数据【原创内容,转载请注明出处】原创内容,盗版必究。源中等待被使用。

【本文受版权保护】【版权所有】唐霜 www.tangshuang.net【原创内容,转载请注明出处】【关注微信公众号:wwwtangshuangnet】原创内容,盗版必究。
projectDetail = source(async (projectId: string) => {
  const [project, transactions] = await Promise.all([
    fetch('project info', projectId),
    fetch('transactions info', projectId),
  ])
  return { ...project, transactions }
}, )

上面这段演示代码演示了我们如何创建这个数【版权所有】唐霜 www.tangshuang.net【本文受版权保护】据源。当然,这不是一个标准做法,因为我们本文作者:唐霜,转载请注明出处。【关注微信公众号:wwwtangshuangnet】的数据可能从各种渠道来,甚至是本地持久化【关注微信公众号:wwwtangshuangnet】原创内容,盗版必究。存储。

【原创内容,转载请注明出处】【访问 www.tangshuang.net 获取更多精彩内容】【转载请注明来源】

接下来,我们要讨论的就是“数据自己的生命【本文首发于唐霜的博客】著作权归作者所有,禁止商业用途转载。循环体”。

本文作者:唐霜,转载请注明出处。【本文首发于唐霜的博客】【访问 www.tangshuang.net 获取更多精彩内容】【本文首发于唐霜的博客】

我们在项目中创建了一个 DataServ未经授权,禁止复制转载。本文作者:唐霜,转载请注明出处。ice 的基类,该类内部实现了上述数据源【作者:唐霜】本文作者:唐霜,转载请注明出处。管理的能力。我们通过一个具体的 Data著作权归作者所有,禁止商业用途转载。本文版权归作者所有,未经授权不得转载。Service 来管理某一组有关联的数据本文作者:唐霜,转载请注明出处。本文作者:唐霜,转载请注明出处。源,它们之间构成了数据自己的生命循环体。【访问 www.tangshuang.net 获取更多精彩内容】本文版权归作者所有,未经授权不得转载。我们来看下例子:

本文版权归作者所有,未经授权不得转载。【原创不易,请尊重版权】【本文首发于唐霜的博客】著作权归作者所有,禁止商业用途转载。
class ProjectService extends DataService {
  // 项目详情
  projectDetail = this.source(async (projectId: string) => {
    const [project, transactions] = await Promise.all([
      fetch('project info', projectId),
      fetch('transactions info', projectId),
    ])
    return { ...project, transactions }
  }, {})

  // 项目列表
  projectList = this.source(async () => {
    const data = await fetchProjectList()
    return data
  }, [])

  // 更新项目基本信息
  updateProject = this.action(async (projectId: string, data: IProjectData) => {
    await httpPut(`.../${projectId}`, data)
    await Promise.all([
      this.renew(this.projectDetail, projectId),
      this.renew(this.projectList),
    ])
  })
}

我们用这个最简单的例子来说明。对于项目而原创内容,盗版必究。转载请注明出处:www.tangshuang.net言,我们往往需要读取项目列表,和某个具体著作权归作者所有,禁止商业用途转载。【本文受版权保护】的项目详情,同时有更新项目基本信息,实际【访问 www.tangshuang.net 获取更多精彩内容】【版权所有】唐霜 www.tangshuang.net上,我们还会有更多,例如获取项目的某些统本文作者:唐霜,转载请注明出处。本文作者:唐霜,转载请注明出处。计数据等等。

转载请注明出处:www.tangshuang.net【原创不易,请尊重版权】【版权所有】唐霜 www.tangshuang.net【本文受版权保护】

当我们更新项目基本信息的时候,我们需要 本文作者:唐霜,转载请注明出处。【未经授权禁止转载】renew 项目详情和项目列表数据。

转载请注明出处:www.tangshuang.net【未经授权禁止转载】未经授权,禁止复制转载。【版权所有,侵权必究】

关键的来了。本文作者:唐霜,转载请注明出处。

【本文首发于唐霜的博客】【本文受版权保护】转载请注明出处:www.tangshuang.net

“我们现在已经对项目相关的数据了如指掌,【本文受版权保护】【版权所有,侵权必究】且它们已经自成体系了。”以往,我们很难表达这种感觉,就是我们还没本文作者:唐霜,转载请注明出处。原创内容,盗版必究。有去考虑交互上的点击提交等操作之前,我们【原创不易,请尊重版权】未经授权,禁止复制转载。的数据自己已经动起来了,而且形成了完整的转载请注明出处:www.tangshuang.net转载请注明出处:www.tangshuang.net闭环。即使脱离了界面的环境,这部分代码都【未经授权禁止转载】【版权所有,侵权必究】非常有价值。对于 ProjectServ本文作者:唐霜,转载请注明出处。【关注微信公众号:wwwtangshuangnet】ice 而言,它可以被实例化,以维持数据著作权归作者所有,禁止商业用途转载。【关注微信公众号:wwwtangshuangnet】,也可以被垃圾回收,释放内存。

本文版权归作者所有,未经授权不得转载。【版权所有,侵权必究】未经授权,禁止复制转载。

我们弱化了“请求”这个动作,强调了“数据【访问 www.tangshuang.net 获取更多精彩内容】【本文首发于唐霜的博客】”这个存在。以往我们要为一个字段的选项列转载请注明出处:www.tangshuang.net【本文受版权保护】表提供数据,我们常常是在组件中创建一个状【关注微信公众号:wwwtangshuangnet】未经授权,禁止复制转载。态,然后用一个请求把列表拉回来,再给这个【本文受版权保护】本文版权归作者所有,未经授权不得转载。状态,把这个状态作为选项列表。但是,在我【访问 www.tangshuang.net 获取更多精彩内容】原创内容,盗版必究。们项目中,我们强调数据,一个数据源是一个【原创内容,转载请注明出处】【未经授权禁止转载】对象,因此可以被作为一件物品进行传递,在转载请注明出处:www.tangshuang.net【作者:唐霜】组件中我们接收这个对象,从该源对象中读取转载请注明出处:www.tangshuang.net【原创不易,请尊重版权】数据,过程中,我们不关心请求这个动作。

【本文受版权保护】【本文首发于唐霜的博客】转载请注明出处:www.tangshuang.net转载请注明出处:www.tangshuang.net未经授权,禁止复制转载。

这种有价值的数据管理,在我们项目中,被独本文版权归作者所有,未经授权不得转载。【原创内容,转载请注明出处】立为数据源管理服务层,它向应用中的很多地【未经授权禁止转载】未经授权,禁止复制转载。方提供数据,不单单是组件里面,还有一些计【版权所有,侵权必究】原创内容,盗版必究。算,一些判断查询,还有模型中提供某个字段本文作者:唐霜,转载请注明出处。【访问 www.tangshuang.net 获取更多精彩内容】的选项列表,它形成了一套独立的服务体系。

【未经授权禁止转载】本文版权归作者所有,未经授权不得转载。【原创内容,转载请注明出处】

业务模块的组织【版权所有,侵权必究】

原创内容,盗版必究。【原创不易,请尊重版权】未经授权,禁止复制转载。【访问 www.tangshuang.net 获取更多精彩内容】【版权所有,侵权必究】

在我们的代码中,我们习惯于将相关的代码放【版权所有】唐霜 www.tangshuang.net本文版权归作者所有,未经授权不得转载。在一起,用以表达某种高内聚的关联关系。在本文作者:唐霜,转载请注明出处。【关注微信公众号:wwwtangshuangnet】我们的项目中,我们有两种组织的单元,一种【关注微信公众号:wwwtangshuangnet】【本文受版权保护】称为Subject,它和具体的界面无关,原创内容,盗版必究。【本文受版权保护】甚至和应用的呈现无关,它具有纯粹的业务描本文版权归作者所有,未经授权不得转载。【原创不易,请尊重版权】述,基于DDD的理念进行建模;另一种是M原创内容,盗版必究。转载请注明出处:www.tangshuang.netodule,是基于某个具体业务而组织在一原创内容,盗版必究。【本文受版权保护】起的代码,狭义的理解,你可以把一个Mod【版权所有】唐霜 www.tangshuang.net【关注微信公众号:wwwtangshuangnet】ule理解为应用中某个模块,例如项目模块本文版权归作者所有,未经授权不得转载。【未经授权禁止转载】、付款模块、签署模块等等。而“数据源管理原创内容,盗版必究。本文版权归作者所有,未经授权不得转载。服务层”虽然被称为一层,但并不代表数据源转载请注明出处:www.tangshuang.net【访问 www.tangshuang.net 获取更多精彩内容】的定义代码被集中放在一起管理,相反,我们【未经授权禁止转载】【原创内容,转载请注明出处】遵循上述组织单元的原则,Subject中【本文受版权保护】转载请注明出处:www.tangshuang.net有自己的数据源管理服务,Module中也【版权所有】唐霜 www.tangshuang.net【本文首发于唐霜的博客】有自己的数据源管理服务。

【本文受版权保护】【原创不易,请尊重版权】未经授权,禁止复制转载。

业务与UI分离著作权归作者所有,禁止商业用途转载。

【未经授权禁止转载】转载请注明出处:www.tangshuang.net【作者:唐霜】原创内容,盗版必究。【关注微信公众号:wwwtangshuangnet】

数据源管理服务层是通用的,且与业务逻辑一【版权所有】唐霜 www.tangshuang.net【作者:唐霜】起,被放在公共实现部分,基于这一设计,不【原创不易,请尊重版权】【转载请注明来源】同端不需要重复实现相同的数据请求,当然,原创内容,盗版必究。【版权所有】唐霜 www.tangshuang.net实际上在我们实现UI时,我们会忘记“请求未经授权,禁止复制转载。未经授权,禁止复制转载。”这个概念,我们不“请求”,而是“索要”著作权归作者所有,禁止商业用途转载。本文作者:唐霜,转载请注明出处。,向数据源索要需要的数据。感官上,我们在本文版权归作者所有,未经授权不得转载。【作者:唐霜】写组件时,很难直接与数据请求打交道,甚至转载请注明出处:www.tangshuang.net【作者:唐霜】脑海中不需要有“请求”的概念,只有当我们转载请注明出处:www.tangshuang.net转载请注明出处:www.tangshuang.net再做骨架屏,需要一个加载状态时,需要数据转载请注明出处:www.tangshuang.net【版权所有】唐霜 www.tangshuang.net源管理服务层暴露一个 loading 的转载请注明出处:www.tangshuang.net【本文受版权保护】状态给到组件使用。

本文作者:唐霜,转载请注明出处。【本文受版权保护】未经授权,禁止复制转载。【原创不易,请尊重版权】【版权所有,侵权必究】

当写组件更纯粹的专注于UI和交互本身,而原创内容,盗版必究。原创内容,盗版必究。不是处理一大堆和请求、数据、业务逻辑相关【版权所有】唐霜 www.tangshuang.net【原创不易,请尊重版权】的东西时,我们的写作会被解放出来。随着数【访问 www.tangshuang.net 获取更多精彩内容】【版权所有,侵权必究】据管理的清晰化,前后端在某种层面上形成了【本文受版权保护】转载请注明出处:www.tangshuang.net一种规范,例如每一个实体需要提供对应的详本文版权归作者所有,未经授权不得转载。原创内容,盗版必究。情、列表、编辑、更新、选项等接口,基于这本文作者:唐霜,转载请注明出处。著作权归作者所有,禁止商业用途转载。一规范,开发效率也会有所提升。

【作者:唐霜】【关注微信公众号:wwwtangshuangnet】【未经授权禁止转载】【本文首发于唐霜的博客】本文作者:唐霜,转载请注明出处。

跨组件数据联动著作权归作者所有,禁止商业用途转载。

【本文首发于唐霜的博客】【本文首发于唐霜的博客】【作者:唐霜】原创内容,盗版必究。

数据的动作,和前端没有直接关系,甚至不一原创内容,盗版必究。本文版权归作者所有,未经授权不得转载。定是前端提交数据带来的,例如系统中有一个著作权归作者所有,禁止商业用途转载。【作者:唐霜】定时机制,在特定时间会更新数据。在上面的【原创内容,转载请注明出处】【本文首发于唐霜的博客】示例代码中,我们有 projectDet【关注微信公众号:wwwtangshuangnet】【转载请注明来源】ail 和 projectList 两个本文版权归作者所有,未经授权不得转载。【版权所有,侵权必究】数据源。我们往往在两个不同的界面中使用这未经授权,禁止复制转载。【原创内容,转载请注明出处】两个数据。但当某个项目发生变化时,这两个著作权归作者所有,禁止商业用途转载。【转载请注明来源】界面都应该被实时更新。

【转载请注明来源】【作者:唐霜】【原创内容,转载请注明出处】
function ListPage() {
  const service = useService(ProjectService)
  const [listData] = useDataSource(service.projectList)
  // 使用 listData
}

在列表页使用列表数据。未经授权,禁止复制转载。

【本文受版权保护】本文作者:唐霜,转载请注明出处。【原创内容,转载请注明出处】著作权归作者所有,禁止商业用途转载。著作权归作者所有,禁止商业用途转载。
function DetailPage() {
  const { projectId } = useRouteParams()
  const service = useService(ProjectService)
  const [detailData] = useDataSource(service.projectDetail, projectId)
  // 使用 detailData
}

在详情页使用详情数据。【版权所有,侵权必究】

【原创内容,转载请注明出处】【访问 www.tangshuang.net 获取更多精彩内容】【作者:唐霜】【本文受版权保护】本文作者:唐霜,转载请注明出处。

我们把系统中的所有 Service 设计【原创内容,转载请注明出处】【本文受版权保护】为可使用单例的对象,所以上面两个组件实际【原创内容,转载请注明出处】【版权所有,侵权必究】上使用了相同的 ProjectServi【作者:唐霜】【作者:唐霜】ce 的单例。

转载请注明出处:www.tangshuang.net原创内容,盗版必究。著作权归作者所有,禁止商业用途转载。【原创内容,转载请注明出处】

上面这两个页面在数据使用上,看似没有任何【本文首发于唐霜的博客】【本文受版权保护】联系,但在 DataService 层面【本文首发于唐霜的博客】【版权所有】唐霜 www.tangshuang.net,它们是有联系的,当项目数据发生变化时,【作者:唐霜】【访问 www.tangshuang.net 获取更多精彩内容】详情和列表都会更新。但对于组件的开发者,【原创内容,转载请注明出处】【本文受版权保护】并不需要去关心这一点,组件开发者只需要关未经授权,禁止复制转载。【关注微信公众号:wwwtangshuangnet】心自己使用了自己需要的数据。这符合 Cl【访问 www.tangshuang.net 获取更多精彩内容】著作权归作者所有,禁止商业用途转载。ear Architecture 的理念【版权所有】唐霜 www.tangshuang.net【本文受版权保护】,我们在写内层的实现时,不应该考虑外层的【作者:唐霜】转载请注明出处:www.tangshuang.net应用。

未经授权,禁止复制转载。原创内容,盗版必究。本文作者:唐霜,转载请注明出处。【本文受版权保护】【转载请注明来源】
function ProjectEditForm(props) {
  const { projectId } = props
  const service = useService(ProjectService)
  
  const handleSubmit = async () => {
    const data = generatePostData()
    await service.request(service.updateProject, projectId, data)
  }

  // ...
}

在表单中更新项目基本信息。【作者:唐霜】

本文作者:唐霜,转载请注明出处。本文版权归作者所有,未经授权不得转载。【原创不易,请尊重版权】【转载请注明来源】原创内容,盗版必究。

当更新完成之后,在 ProjectSer【本文首发于唐霜的博客】【原创不易,请尊重版权】vice 里面,它调用 renew 来更未经授权,禁止复制转载。著作权归作者所有,禁止商业用途转载。新当前项目和项目列表的数据,从而让 Li转载请注明出处:www.tangshuang.net【访问 www.tangshuang.net 获取更多精彩内容】stPage 和 DetailPage 本文作者:唐霜,转载请注明出处。未经授权,禁止复制转载。作出对应的变更。上面三个组件中,我们不需【原创内容,转载请注明出处】【关注微信公众号:wwwtangshuangnet】要去处理它们相互之间的联动关系,因为这个著作权归作者所有,禁止商业用途转载。未经授权,禁止复制转载。动作是由数据层完成的,而非表现层。而且,本文作者:唐霜,转载请注明出处。原创内容,盗版必究。实际上,除了表现层可能用到这些数据,逻辑本文作者:唐霜,转载请注明出处。【作者:唐霜】层也可能用到,这意味着除了界面的变更,实【版权所有,侵权必究】【本文首发于唐霜的博客】际上我们在逻辑层的处理也可以被重新执行。

本文版权归作者所有,未经授权不得转载。本文作者:唐霜,转载请注明出处。本文版权归作者所有,未经授权不得转载。【版权所有】唐霜 www.tangshuang.net

前端项目的分层设计让不同部分的职责更单一本文作者:唐霜,转载请注明出处。原创内容,盗版必究。,前端框架则负责在各个层之间建立响应式关【原创内容,转载请注明出处】【原创内容,转载请注明出处】系。

著作权归作者所有,禁止商业用途转载。【原创内容,转载请注明出处】【版权所有】唐霜 www.tangshuang.net著作权归作者所有,禁止商业用途转载。

效果预览本文作者:唐霜,转载请注明出处。

本文作者:唐霜,转载请注明出处。【未经授权禁止转载】本文作者:唐霜,转载请注明出处。【本文首发于唐霜的博客】【本文首发于唐霜的博客】

在投资信息中,投资方和被投方有约见记录,著作权归作者所有,禁止商业用途转载。【本文首发于唐霜的博客】录入约见记录是一个非常简单的操作。我们来未经授权,禁止复制转载。【关注微信公众号:wwwtangshuangnet】看下,在这个录入操作下,使用上述方案的实本文版权归作者所有,未经授权不得转载。未经授权,禁止复制转载。际效果。

【作者:唐霜】本文版权归作者所有,未经授权不得转载。【作者:唐霜】著作权归作者所有,禁止商业用途转载。

 本文作者:唐霜,转载请注明出处。

本文作者:唐霜,转载请注明出处。【本文受版权保护】著作权归作者所有,禁止商业用途转载。

约见记录列表陈列了该公司已有的约见,当点【版权所有】唐霜 www.tangshuang.net转载请注明出处:www.tangshuang.net击添加约见时,会进入到一个新页面(新路由著作权归作者所有,禁止商业用途转载。原创内容,盗版必究。),完成添加之后,会回到这个页面,而回来【本文受版权保护】【本文首发于唐霜的博客】时,我们新增的约见被读取了出来。在两个页未经授权,禁止复制转载。本文作者:唐霜,转载请注明出处。面之间进行不同的数据存取操作,虽然动作本【关注微信公众号:wwwtangshuangnet】著作权归作者所有,禁止商业用途转载。身是分开的,但是它们存在内在的联系。在 【本文受版权保护】【本文受版权保护】DataService 中,将这两种内在【版权所有,侵权必究】本文作者:唐霜,转载请注明出处。联系绑定在一起,因此,完成提交操作时,列【本文受版权保护】【原创内容,转载请注明出处】表数据源需要随即进行更新。但在组件开发时【本文受版权保护】原创内容,盗版必究。,我们不需要去关心这种内在联系,因为对于著作权归作者所有,禁止商业用途转载。【作者:唐霜】列表组件而言,我们不需要也不应该想着数据【关注微信公众号:wwwtangshuangnet】著作权归作者所有,禁止商业用途转载。的更新,同时,添加表单组件也不可能穷举出著作权归作者所有,禁止商业用途转载。转载请注明出处:www.tangshuang.net自己需要联动触发哪些组件更新。对于这两个【原创不易,请尊重版权】【本文首发于唐霜的博客】组件而言,它们只专注做自己的事情,把数据本文版权归作者所有,未经授权不得转载。【本文首发于唐霜的博客】的联动交给数据源管理服务层。

原创内容,盗版必究。【转载请注明来源】【关注微信公众号:wwwtangshuangnet】著作权归作者所有,禁止商业用途转载。本文版权归作者所有,未经授权不得转载。

结语【本文受版权保护】

【版权所有】唐霜 www.tangshuang.net【原创不易,请尊重版权】未经授权,禁止复制转载。原创内容,盗版必究。原创内容,盗版必究。

在腾讯投资相关系统中,经常使用这种模式。【版权所有,侵权必究】转载请注明出处:www.tangshuang.net无论PC端还是APP端,使用这种模式进行【转载请注明来源】原创内容,盗版必究。开发可以让我们比较轻松的完成跨界面的数据【本文受版权保护】著作权归作者所有,禁止商业用途转载。联动。这种模式,首先将数据管理从UI编程【转载请注明来源】【本文受版权保护】中独立出来,建立一层数据源管理服务层,并著作权归作者所有,禁止商业用途转载。著作权归作者所有,禁止商业用途转载。且在该层中构建了数据自身的生命循环体;接著作权归作者所有,禁止商业用途转载。转载请注明出处:www.tangshuang.net着,借助框架的能力,让数据管理工具具备响【本文首发于唐霜的博客】【未经授权禁止转载】应式能力,并通过hooks封装,让组件可【本文受版权保护】本文作者:唐霜,转载请注明出处。以在数据发生变化时进行更新;最后,在UI未经授权,禁止复制转载。本文作者:唐霜,转载请注明出处。编程时,开发者不需要思考自己的动作会触发【原创不易,请尊重版权】【版权所有】唐霜 www.tangshuang.net其他哪些组件的变更,也不需要记着自己需要著作权归作者所有,禁止商业用途转载。【原创不易,请尊重版权】依赖其他组件的动作来重新渲染,开发者专注本文作者:唐霜,转载请注明出处。【作者:唐霜】完成自己的实现,而把这种跨组件的联动交给【版权所有,侵权必究】【原创内容,转载请注明出处】数据源管理服务层和封装的hooks函数来【转载请注明来源】【转载请注明来源】实现。前端数据层在以往我们很少去深入思考【访问 www.tangshuang.net 获取更多精彩内容】本文版权归作者所有,未经授权不得转载。,无非就是请求数据,但是,在这种数据管理著作权归作者所有,禁止商业用途转载。【原创内容,转载请注明出处】模式下,我们实现了架构上的分层,让数据自【本文首发于唐霜的博客】本文版权归作者所有,未经授权不得转载。治,把数据的管理从组件体系中分离出来,这【本文受版权保护】【原创内容,转载请注明出处】使得数据本身更封闭更聚合,同时当为其他部转载请注明出处:www.tangshuang.net【关注微信公众号:wwwtangshuangnet】分(UI或某些逻辑判断)提供服务时,具有【作者:唐霜】【本文首发于唐霜的博客】更开放更简洁的出口。当然,这一模式在某种【版权所有】唐霜 www.tangshuang.net【作者:唐霜】程度上,依赖项目代码的整体架构设计,没有著作权归作者所有,禁止商业用途转载。【未经授权禁止转载】分层的架构设计理念,很难适应这种模式,同本文作者:唐霜,转载请注明出处。转载请注明出处:www.tangshuang.net时,它也依赖于某些技术上的实现,不然没法【转载请注明来源】【转载请注明来源】做到那么高程度的封装。不过,相信这种设计原创内容,盗版必究。本文作者:唐霜,转载请注明出处。在很多业务系统中,需要跨组件跨界面实现数【访问 www.tangshuang.net 获取更多精彩内容】著作权归作者所有,禁止商业用途转载。据联动的场景下,具有不错的借鉴意义。

【本文首发于唐霜的博客】【版权所有】唐霜 www.tangshuang.net【作者:唐霜】