ascoders / weekly

前端精读周刊。帮你理解最前沿、实用的技术。
28.41k stars 3.23k forks source link

关于前端数据流和一站式框架的思考 #204

Closed Tan90Qian closed 5 years ago

Tan90Qian commented 5 years ago

在周刊第49篇精读《Compilers are the New Frameworks》中提到了UmiJs等框架是一种自带了约定式路由、数据流的一站式框架,并且说明umi并不限制数据流的选型。 但我的觉得这其实只是针对于传统模式下的数据流设计方案来说,umi并没有进行限制。 假如采用类似于第5篇精读民工叔单页数据流方案的MVI数据流方案,形成这种形式的组件设计,实现局部(作用范围)的全局(生命周期)数据流模式,那umi这类框架所实现的约定式路由就无法满足了。 而这种数据流模式能够实现更细粒度的受控组件通用逻辑复用,并满足多组件相关的交互需求,非常适合用于管理后台这种组件复用率高的项目中。

Tan90Qian commented 5 years ago

同时对于koa、egg这样的node框架,约定式的项目结构并自动装入ctx上下文属性中,我觉得其实会发生一种跟史前前端项目一样的问题:无法在项目编译/启动阶段就将“api不存在”的问题暴露出来,而是要到运行时,触发了对应的api后才会发生报错以致于阻塞后续流程。反而是express这种显式引入资源的模式更稳定一些。而且约定式框架在进行单元测试方面也会因为内部依赖难以mock而比参数依赖注入的模式更麻烦一些。 除了“专注于业务逻辑编写”外,约定式项目结构的核心优势是什么?

ascoders commented 5 years ago

我写的 pri 也支持插件拓展约定数据流。这么做的原因是,业务相关数据流建议放到全局管理,此时利用框架自带的数据流即可;通用的组件可以用自己的局部数据流,如果用到 Hooks 配合 useContext 更方便。

Tan90Qian commented 5 years ago

v16.9发布前,不管是enzyme还是test-library/react的测试工具,都无法正确测试使用了useContext钩子的组件。这里是当时提的issue。 因为发生了这个问题,导致state与context存在关联的SFC组件无法编写测试用例,因此就基本放弃了hooks的使用。转而采用了现在这种组件内状态外提至mobx,通过工厂函数注入SFC的模式,context仅用于面包屑等纯UI的动态数据的展示与交互。至于与业务相关联的context则转为通过工厂函数的参数注入的形式得以使用,而且这种context极为稀少。 在这种数据流模式下,一个组件将store、sfc与factory全部暴露出去,且factory可以选择性的只返回聚合后的实际组件,或者将该组件的store实例一起返回(通过对象的形式),达到更细粒度的且更自由的复用方式。但自由的代价就是,路由的处理会更加麻烦一些,因为factory函数会需要诸如全局store父级factory提供的api等参数,很难以约定式的统一处理。

Tan90Qian commented 5 years ago

比如说一个查询列表(表格)页面,从结构上可以分为header的展示区和可能存在的tabsbody的formbody的table以及pagination。 因为antd的通用组件提供了受控与非受控两种使用方式,但通常业务上都会使用受控的形式。那么可以针对tabs、form、table和pagination分别创建对应的通用store与presenter(当然store与presenter也可以合并成一个),它们基本可以与antd组件的api相对应。然后针对单个页面编写一些业务关联的store与presenter,比如data和options的loading statefetch action,然后工厂函数中进行聚合,并创建页面对应组件并返回 那么对于多个类似查询列表页,只需要修改变化的部分(如columns的配置、form的配置)等,即可完成一个页面组件的创建。不需要像传统的模式那样,因为交互对数据的影响而把数据全部提升到子组件的公共父组件中。

ascoders commented 5 years ago

@Tan90Qian 全局数据流的好处是模型的整体性,甚至可以不依靠 UI 单独完成数据业务逻辑的开发,这样的数据框架对业务代码起到一定约束作用。当然对于较为独立的业务模块,也可以利用 Context 管理自己的局部数据流嵌入到项目中,也能 work 良好,这两者可以配合使用。

Tan90Qian commented 5 years ago

@ascoders 传统数据流模式毕竟是经过实践考验的,肯定是有它的优势,这毋庸置疑。 我想表达的只是:我之前列举的这种基于工厂函数的局部数据流方案,与umi这样使用约定式路由的一站式框架并不契合。 因为这种模式是非常自由的,也就导致了它跟“约定”天然像违背。它有它自己的约定:组件由工厂函数factory、状态实例store和SFC组合而成,通过在工厂函数内进行逻辑聚合来实现业务组件的实际功能。但这种约定是与路由无关的,甚至是相背离的。 换言之,一站式框架其实依然有它的局限性,可能它在数据流之外依然存在着其他领域的缺陷。

ascoders commented 5 years ago

一站式框架至少有两个目的:

  1. 把好的开发模式固化下来,提升开发效率。比如全局数据流,如果你有全局数据流需求,那么可以很轻松的集成,如果想用局部数据流那忽略这个功能即可,不能说是一种缺陷。
  2. 统一开发环境,实现千人一码,哪怕外包同学写的代码也可以轻松拿过来维护是一个目标。
Tan90Qian commented 5 years ago

对于第二个目的千人一码我是认可的,但第一点我觉得有一些问题:

  1. 没有任何数据流模式能完美适应所有类型的项目。比如电商等常见toC项目,前端数据流是很薄的一层;toB的中台项目若存在复杂的业务逻辑(如多层嵌套的表单、流程图等)则相对厚一些;对于web端协同工具或者设计等专业向产品,则前端数据流更加复杂。而所谓好的开发模式往往只能在某一类或者几类项目中达到非常好的开发体验,而在其他类型的项目则略显别扭。很难有银弹级的数据流方案能完美契合所有类型的前端项目。
  2. 既然要把开发模式固化下来,那么往往需要将不变的部分抽象封装起来。当抽象的程度随着版本迭代逐渐提升时(如ant-design-pro从v1发展到v4),很多一站式框架的特性便不是你想不用就能不用的了。放弃这些框架提供的特性付出的代价甚至可能比从头搭一个项目少不了多少。
ascoders commented 5 years ago
  1. 数据流可以有弹性的,比如简单项目用全局模式,复杂项目用局部管理模式(类似 model 和 page 在一起,models 可以拆分多个文件夹),还是可以 cover 住不同复杂度的。
  2. 换一个特性等于牺牲了开发效率,对团队协作来说并不可取,如果一个新技术带来的提效真的较大,框架可以大版本升级支持它,但要做好升级迁移指南。
Tan90Qian commented 5 years ago

对于通过大版本升级来支持新技术、新特性的方式,如果新特性和已有特性的分歧较大,在框架内部进行兼容处理的成本较大,是否会导致一定版本的迭代之后框架自身的难以维护?

ascoders commented 5 years ago

@Tan90Qian 分歧过大就一刀切,不用兼容了,一般不会遇到这种情况。

atian25 commented 5 years ago

这时候应该检讨为啥会有这么大的 break change: