fish_redux是内聚闲鱼技术团队打造的开源flutter应用开发框架,旨在解决页面内组件间的组件做闲造开高内聚、低耦合问题。鱼打源高开源地址:https://github.com/alibaba/fish-redux 从react_redux说起 redux对于前端的效方同学来说是一个比较熟悉的框架了,fish_redux借鉴了redux单项数据流思想。内聚在flutter上说到redux,组件做闲造开大家可能第一反应会类比到react上的鱼打源高react_redux。在react_redux中有个重要的效方概念——connect, connect([mapStateToProps],内聚 [mapDispatchToProps], [mergeProps], [options]) 简单得说,connect允许使用者从Redux store中获取数据并绑定到组件的组件做闲造开props上,可以dispatch一个action去修改数据。鱼打源高 那么fish_redux中的效方connector是做什么的呢?为什么说connector解决了组件内聚的问题?我们应该如何理解它的设计呢? connector in fish_redux 尽管都起到了连接的作用,但fish_redux与react_redux在抽象层面有很大的内聚不同。 fish_redux本身是组件做闲造开一个flutter上的应用框架,建立了自己的鱼打源高component体系,高防服务器用来解决组件内的高内聚和组件间的低耦合。从connector角度来说,如何解决内聚问题,是设计中的重要考量。 fish_redux自己制造了 Component树, Component聚合了state和dispatch,每一个子 Component的state通过 connector从父 Component的state中筛选。如图所示: 可以看到,fish_redux的connector的主要作用把父子 Component关联起来,最重要的操作是filter。state从上之下是一个严谨的树形结构,它的结构复用了 Component的树形结构。类似一个漏斗形的数据管道,管理数据的分拆与组装。它表达了如何组装一个 Component。 而对于react_redux来说,它主要的香港云服务器作用在于把react框架和redux绑定起来,重点在于如何让React component具有Redux的功能。 从图中可以看到,react_redux和React是平行的结构,经过 mapStateToProps后的state也不存在严谨的树形结构,即对于一个React component来说,它的state来自于Redux store而不是父component的state。从框架设计的角度来说,react_redux最重要的一个操作就是attach。 源码分析 说完概念,我们从源码的角度来看看fish_redux中的connector是如何运作的,以fish_redux提供的example为例。 在ToDoListPage的构造函数中,向父类构造传递了一个 Dependencies对象,在构造 Dependencies时,参数 slots中包含了名叫"report"的服务器托管item,注意这个item的生成,是由一个 ReportConnector+ ReportComponent产生的。 从这里我们得出一个简单却非常重要的结论: 在fish_redux中,一个Dependent = connector + Component 。 Dependent代表页面拼装中的一个单元,它可以是一个 Component(通过buildComponent函数产生),也可以是一个 Adapter(由buildAdapter函数产生)。这样设计的好处是,对于View拼装操作来说, Dependent对外统一了API而不需要透出更多的细节。 根据上面我们得出的结论, connector用来把一个更小的 Component单元链接到一个更大的 Component或 Adapter上。这与我们之前的描述相符合。 connector到底是什么? 知道了 connector的基本作用,我们来看一下它到底链接了哪些东西以及如何链接。 先来看一下ReportConnector类的定义: ReportConnector继承了 ConnOp类,所有 connector的操作包括+操作,都来自于 ConnOp类。 set/get 既然是数据管道,就会有获取和放置。 set函数的入参很好得表达了 T和 P的意思,即把一个 P类型的 subState合并到 T类型的 state中。 再回头看 get函数,就很好理解了, get函数表达的就是如何从 T类型的 state中获取 P类型的 subState供 Dependent使用。 operator + +操作符的重载是我们最初看到connector作用的地方,也是connector发挥作用的入口。 Logic是 Component和 Adapter的父类,它表示页面组装元素的逻辑层,里面包含了 reducer/ effect/ higherEffect等与逻辑相关的元素以及它的组装过程。 operator +调用了 createDependent函数,接着会调用到 _Dependent类的构造函数,这里将 logic和 connector放入 _Dependent内部,在后面fish_redux对 Component组装的过程中,connector会随着外部对 _Dependent中函数的调用发挥作用。 connector正式登场 铺垫了这么多,是该connector正式发挥作用的时候了。 get 我们以 Component为例,会调用到 _Dependent的 buildComponent函数: 这里的 logic实际就是一个 Component对象,在调用 Component的 buildComponent函数的时候,使用 get函数从一个大的父state中获取到当前 Component需要的数据集。接下去,这个变换后的子state将被用在例如 ViewBuilder或 Redcuer函数中。 这是connector在数据获取上的作用。 set 还是在 _Dependent类里面,看 createSubReducer函数: 首现从一个 Logic(这里实际上是一个 Component)对象中获取到外部设置进来的 reducer,接着调用 subReducer返回一个 SubReducer对象。 SubReducer是一个被wrap后的 Reducer。 subReducer的实现在 MutableConn中, ConnOp继承了 MutableConn类,也获得了这个能力。 它首现通过 get函数得到一个变换后的数据集 props,接着调用原始的 reducer函数进行逻辑处理,这里有一个优化也是 SubReducer的作用,如果数据集在经过 reducer处理之后发生了变化, 并且state已经被copy过一次了(isStateCopied==true),就直接把 newProps通过 set函数更新到state中去。(这个优化可以防止多个子state发生变化的时候父state被拷贝多次)。 至此,connector在数据更新上的作用也体现出来了。 ReportConnector 最后,就好理解ReportConnector的实现了: classReportConnectorextendsConnOp<PageState, ReportState> { @override ReportStateget(PageState state) { finalReportState reportState = ReportState(); reportState.total = state.toDos.length; reportState.done = state.toDos.where((ToDoState tds) => tds.isDone).toList().length; return reportState; } @override voidset(PageState state, ReportState subState) { } } 很明显的,在 get函数中, ReportState从 PageState中获得了total/done字段。 总结 闲鱼客户端的详情页完全使用了fish_redux进行了重构,通过高内聚的 Component+connector形式,使得 Component可以被大量复用,很好得支持了5种类型的详情页。未来我们会基于fish_redux强大的扩展能力制作更多组件来满足不同业务对于框架的需求。