1。什么是数据流,什么是流?在数学中,流将“随时间变化”的一般思想以数学方式形式化。目前,将流定义为响应时间变化的集合。什么是数据流根据前面的定义,数据流是随时间变化的数据集合。前端是针对现状的。在mvvm模式下,数据就是页面。在大多数情况下,数据保持不变,页面保持不变。那么我们来转换一下,数据==>页面,数据流是否可以等同于一组页面的变化。这是我们的业务逻辑。当然,之前的假设经过多次改造,其实也有很多漏洞和错误。但可以作为一个简单的参考。这不是数据流的定义,只是作为介绍。2、目前的前端数据管理模型目前的三大数据管理方式是函数式、不可变和模块化。典型实现:Redux。反应性、依赖性跟踪。典型实现:Mobx。反应式,以流的形式实现。Rxjs,xstream。Redux模式(reduck)redux模式的一般用法是管理整个应用程序的全局状态。这只是作为一种工具来提高跨组件通信的能力。redux的思想是将数据作为一个独立于组件的数据仓库来保护,保证数据的稳定性和可靠性。可以简单理解为redux是一个全局使用的带保护的Context(useContext)。为原始提供数据保护(dispatch+reducer),更改只允许使用dispatch。可以保证溯源,数据来源清晰,可以很好的隔离副作用。使用方面:优点:数据隔离,数据变化可追溯。缺点:多个redux直接完全隔离,难以小型化,action方法容易扩展。商业用途:对于大型项目,拆分设计理念不足,个人对store数据的把控不友好,容易造成理解困难和逻辑修改困难。Mobx模式mobx引入了一个新的思路,以数据为源,当数据发生变化时,通过计算状态,页面发生变化,根据可观察的,自动根据依赖进行更新。虽然有action,但是仍然没有强制分离副作用。mobx就像绑定数据和组件形成依赖关系,自动订阅自动发布,组件随状态变化而变化。直接绑定逻辑和视图应该是一种非常高效的情况,但是由于组件比较深入,副作用的处理还是不够清晰,个人控制不够友好。Stream模式(rxjs)rxjs有点类似于mobx。rxjs可以随意将所有数据拆解组合成一个新的节点,可以简单理解为将redux的状态打散成多个数据节点,每个任意节点都可以进行类似computed的计算生成新的节点。与redux模式相比,flow模式没有action规范,而是定义了要改变的节点范围,只能改变定义好的entry节点(一个flow的起始节点)。rxjs不具备mobx从数据变化到页面变化的功能,可以使用useState和useEffect或者现成的三方库rxjs-hooks来实现。rxjs的好处是,提取完所有的数据源,剩下的都是逻辑问题,在提取数据源的时候副作用已经剥离干净了(因为外部的副作用数据也可以提取到rxjs节点),并且rest就是通过api和purefunctions写具体的逻辑。并且由于API数量众多,拆解可观察节点的成本极低,所以逻辑拆分非常容易,可读性非常高。rxjs有推拉的概念。当正常的逻辑很流畅的时候,程序的代码应该是每次节点变化都会推送下一个节点的执行。数据流在rxjs中流式处理后,组件只需要对head节点读写数据,对tail节点直接读取数据,大部分逻辑都从组件中提取出来。使用方面:优点:逻辑拆分容易,纯函数式编程。缺点:数据拆分复杂,重新设计,api学习理解成本高。理想情况下,页面是无状态组件,其行为会改变数据。数据变化触发逻辑变化,逻辑改变数据。数据流回页面,是一个整体闭环,以数据为核心,完美实现数据驱动的页面。NovaRecoil反冲是facebook官方推荐的一个状态管理库。作为“新成员”,后坐力相对于之前的三种状态管理方式做了很多取舍。它有节点、atom(原子数据)和selector(派生数据)的概念,但与mobx不同的是,recoil是基于Immutable(不变)模式。recoil的基本思想是原子数据之间没有关系,所有产生的相关数据都是由选择器产生的。当原子发生变化时,相关的选择器也会随之变化。这与响应式流的思想是一致的。后坐力的优点是适合反应。recoil的实现可以看作是useMemo封装的context。使用api可以满足只读和只写的拆分,可以非常适合优化渲染,减少无用渲染。上面说的前三个数据流没有好坏之分。它们只是在不同的场景下各有优势。3、理想的源数据编程数据与数据的关系数据不是凭空产生的,数据可能会产生新的数据。数据之间实行最小可用性原则,分而治之,更有利于我们的开发和维护。个人将生成数据的起始数据定义为源数据。有些数据是可以相互转换的,那源数据怎么定义???定义源数据从组件(页面)的角度看数据。在大多数面向接口编程的情况下,前后端之间的数据交互只是接口。又因为所有真实的数据都是从服务器获取的,所以下意识的就从服务器接口作为数据入手。数据处理的环节长了,范围变大了,对个人的理解要求很高。就我个人而言,我认为这对于大型应用程序来说是不健康的。面向数据编程将接口请求分离,只关心组件状态。对于组件来说,不区分任何数据,数据来了就渲染。用户可以与之交互的单个数据可以定义为源数据。因为前端接口请求也是来自于用户的信息请求。对于应用来说,只有用户的操作是不可预测的,其余的操作都是可控的。如果把可控的逻辑封装和提取出来,我们在管理的时候就不需要直接去感知这些了。我们直接面对的是用户的操作和页面的响应。例如:对于单个列表页面,用户选择的过滤项是源数据,接口请求返回的列表数据是派生数据,接口请求产生的页面加载状态也可以是派生数据或源数据。redux数据模型在使用redux时没有很好的处理数据的层次关系,导致store中的数据池越来越大,没有很强的层次概念。这也是redux小型化难度大造成的。有时跨层级的数据会下意识的存储在store中。其次,redux不能很好的描述数据与数据之间的关系。有人说computed可以描述数据与数据之间的关系。简单意义上是没有问题的,但是由于computed的限制,无法跨redux支持。如果要使用,必须将所有源数据汇集到同一个redux中,这违背了最小可用性原则。是因为redux的设计模式不够灵活,导致页面或组件内部大量数据与数据之间的转换逻辑积压,对视图层是一种负担。为了方便响应式流数据模型的理解,数据处理可以理解为之前计算过的。每个节点都可以随意生成一个新的节点,但是触发整个流的变化只会在初始节点(源数据)节点,使用整个流的结果。在流模式下,页面外层的组件或数据没有层次结构。每个节点都处于同一级别。如果是分层的,可以在业务上分层。通过不断的拼接,将业务逻辑串联起来,从而得到你想要的结果,与原来分散的逻辑相比,串行流逻辑在可读性上也更好。流式数据的好处是拆分的代价极低,更符合我们的思路。代码块被拆分,让每一小块逻辑拆分成一个节点,通过不断的拆解,逻辑复杂度变得非常复杂。低,但不会因为拆的太多而散落。固化数据没有层次,离散数据可以自由定义和拼接。对于离散数据,逻辑是流式传输的。数据灵活之后,逻辑收敛,不会散乱,这样才能精准的使用数据。reducx和rxjs的数据管理范围总结综上所述,其实不同模式带来不同场景的应用,redux应用快,开发快,数据变化稳定,mobx响应变化。每个都有自己的特点。写代码的时候,感觉自己在构建动画的每一帧(view),每一帧为什么要变化(写逻辑,事件)都要给出感觉。逻辑和视图混合在一起,对于整个控件来说很难处理,就像你需要掌握整个动画的变化一样,脱离了视图和逻辑,逻辑只需要改变数据,而视图只需要更改相应的数据即可。参考文章FlowingData-使用RxJS构建复杂单页应用的数据逻辑精读《前端数据流哲学》
