介绍:实际项目中数据流的最佳实践。数据流是前端一直存在的问题。我们的项目积累了一套最佳实践。如果您有任何问题,请随时讨论。在老的Done项目中,代码复杂度高,已经到了“一根毫毛影响全身”的地步,技术债极高的情况。由于旧代码的“复杂性”,实现一个简单的功能需要比正常时间多2到3倍的工作量。就像下图一样。如果我们仔细分析现有的业务,我们会得到以下业务特征:强字段(例如:项目/文件/团队/用户字段,很多组件会同时调用某个字段的方法,静音/点赞/转账Project...)单页多且复杂,组件过多,多层嵌套组件之间通信多。业务实现了从0到1,目前处于从1到n的阶段。这个阶段会根据用户的建议进行大量的产品交互调整,打磨品质。需求量增加,前端变化大,前端人力瓶颈大。基于以上业务特点,我们分析一下当前项目存在的问题:模板代码过多,影响开发效率和可维护性。数据流盘旋成网络调用(强耦合),代码复杂度急剧增加。全身数据全球化原始数据与展示数据转换UI与数据逻辑耦合,复用度低针对以上特点和问题,我们有以下诉求:简单高效拒绝模板代码降低代码复杂度(减少联动影响)提高复用性极大复用UI/逻辑层复用数据转换基于以上问题和分析,下面将逐步推导出新的架构图&技术选型。问题分析1.UI与数据逻辑耦合复用度低&原始数据与展示数据转换原来整体架构如下:Store和View层混合在一起处理用户行为和业务逻辑,耦合度高,复用率低.架构演进:改进:独立Store层,封装与应用和业务逻辑相关的数据和对数据的处理方法,在这里独立编写逻辑,支持多个组件复用一个逻辑,独立于视图层,一个逻辑可以在多个组件中复用。众所周知,分层是为了解耦。假设Store层发生了变化,那么view层就不用改了,只需要修改Store层,减少了改动次数,提高了开发效率和可维护性。但是,我们还是会遇到一些场景,当后台接口的字段变化的时候,要改的地方太多了。同一个界面出现在不同的地方。由于多人维护,会经过多次数据转换,最终映射到前端界面。因此,我们再增加一个API防腐层来处理前端接口和后端接口之间的数据转换。这样,一旦数据结构发生变化,我们只需要在API层修改即可,无需关注多个接口组件。改进点:独立的API层,链接后端服务和前端服务,如果后端接口发生变化,可以在这里统一修改,不需要修改多处代码,方便在这一层进行数据检查,以及防止后端返回错误的字段等。通过上面的分层,可以轻松解决以上两个问题。2、数据流网络调用我们对Store和View进行了分层,但是随着项目的迭代,又出现了下面的情况,数据流是网络调用。举个例子:在旧代码中,工作站会调用队内数据,修改队内数据。甚至在接口回调之后,也会在各个地方制作与静音接口相关的相关数据。被修改。这会带来一个问题。由于是全局共享的Redux,我们在A处调用这个方法,B处的界面会在这里更新(B处不会出现在用户界面上),也就是修改A处的action,我们还需要要关注B、C、D……这些地方(其实其他地方我们不需要关注)我们知道分层可以解耦,对于降低复杂度是非常有效的。那我们是不是也可以把Store分层,约束它们互相调用呢?架构演进:重构后,同级store不能互相调用。如果需要调用,则意味着必须将商店提升到上层(详见下文)。改成这种方式后,整个Store层也清晰了。而且不会随着业务迭代越来越复杂。重构后,我们只需要用下面这几行简短的代码就可以实现:每个视图组件(A/B/C/D)获取调用“mute”的回调结果,自行更新界面。这样一来,从原来的一段代码需要关注N个地方,现在只有一段代码需要关注一个地方,大大降低了代码的复杂度。3.数据全球化举个例子:在非父子组件之间共享和传递一些状态,我们将使用状态提升来解决这个问题。但是如果此时组件之间嵌套太深,中间经过的组件会帮忙传递这些无用的props,如果需要传递参数或者添加props,需要修改A、B、中间组件*n个地方。那么这个时候,我们会把这些状态放到全局的redux中。但这会导致其他问题。对于一些临时维护的状态,比如常见的中景和背景:A组件控制同层弹窗组件E的显示和隐藏状态,这些状态对用户来说并不重要。需要保存,是一次性的。这个时候因为中间的组件太多了,所以我们放在redux里面。随着时间的推移,redux中的action越来越多。慢慢地,我们每次修改,都需要确认是否也影响到其他组件(其实这个动作只会在本模块使用,其他模块不需要关注)。对于Redux学派的数据流管理,都是中心化的。看了大部分中后端产品,需要全局共享的数据并不多,一般只有用户信息。在这种情况下,我们将目光投向了自然支持多商店的mobx。那么如何设计店铺呢?高内聚对于提高项目的可维护性自然是一件好事,但是如果粒度控制不好,就很容易出问题。比如我们严格遵循React官方的指导方针:如果多个Components需要交互,那么状态(即:data)就维护在这些Components的最小约定父节点上。按照这个划分,我们的程序会出现几十上百个约定节点,随着项目的迭代推进,以前是A、B共享,后来变成A、B、C共享。最大的约定节点需要升级。在多人协作的情况下,升级变更太多容易造成项目不稳定,所以我们划分了以下三个存储层,最小粒度的存储是模块存储。映射到具体项目的Store映射如下:4.DomainModelDDD我们如何设计Store?我们使用领域驱动设计或DDD。为什么需要DDD?在传统的前端开发过程中,前端和业务是通过可视化稿连接起来的。视觉稿一旦改动,就意味着大量的修改成本。目前产品已经到了1~n阶段,视觉稿需求稿的变化在所难免。一个复杂的产品需要多人协作。如果每个人都按照视觉稿来设计Store,那么重复的逻辑就会越来越多,技术债也会越来越多。如果我们采用DDD来贴合业务,比如我们抽象出一个领域模型“文件”,其中存储文件的相关操作:“修改文件名,删除文件,移动文件……”,有这样一个稳定的领域model,视图层只需要实现可视化草稿和组装业务逻辑。它具有很强的灵活性,就像搭积木一样。底层领域模型不需要改变,只需要改变交互变化或视图。大大提高了开发效率和可维护性。例如,随着项目的迭代,对文件的相关操作:删除、移动等,已经沉积在领域模型中。如果此时产品发生变化,则删除条目发生变化,或者增加新的删除。entry,那么我们只需要修改view组件,然后在需要调用的地方调用domain对应的action,然后按照domain进行划分,Stores之间的界限也更加清晰。注意:DDD不是一个框架,而是一种架构思想。所以在开发前端之前,需要先提炼需求,设计好Store再开发。项目中已经沉淀的领域改进点:根据Mobx官方的指导和建议,除了页面的松散状态外,还会根据整个业务特性沉淀一些通用的领域模型,可以注入到根据不同页面的需要对应PageStore。.领域驱动设计的概念已经在我们的业务中出现了几个月。参与开发的同学一致认为,在前端人力瓶颈较大的情况下,后端同学也参与编写了Store,Store和view并行开发,从串行开发到并行,大大提高了整体开发效率。写了这么多技术选型,新的系统架构需要具备以下特点才能让系统走得更快更远:合理的分层,UI/逻辑分离,复杂项目存储的粒度细化很重要,领域模型DDDReject模板代码,提高开发效率,面向未来&兼容旧代码——支持hook&class,更好的ts支持我们期望有这些特性,然后一一对比。unstated-next不知道要写多少个provider,写法太灵活,组织成领域模型需要改造很多(我在一个小项目中尝试过,最后随着业务越来越多而放弃了)更复杂。)需要改造旧的业务逻辑。一旦上课,就得回到原来的写法。快速迭代业务时,不可能停下来,将它们一一转化为钩子。redux中大量的模板代码,过于灵活。Redux调用(mesh调用)TS支持高转换成本。为什么选择mobx??有限/无模板代码?面向未来&兼容旧代码--支持hook&class?非常方便业务分层和领域模型设计?TS支持0成本?天然支持多态Store,去中心化更多方便?需要显示DI,解决了mesh调用的问题。业界对Mobx的缺点也说了很多,无外乎以下几点:state可以随意修改——解决办法:开启严格模式限制,并且只能在Store中Modifications,不能在view中修改component不支持hooks,mobx-react-lite已经支持了以上所有的原因,如果为了这些特性,重新造一个轮子或者改造一个轮子,成本远比直接使用mobx的威力大。因此,在考察了多种数据流解决方案后,我们选择了Mobx来支持我们的上述架构。总结没有最好的技术方案,只有最适合业务的技术方案。我们从每个“业务痛点”推导出一套解决方案,并在实际项目中运行了几个月,取得了不错的效果。作者:sheet原文链接本文为阿里云原创内容,未经允许不得转载
