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为例。
class ToDoListPage extends Page<PageState, Map<String, dynamic>> {
ToDoListPage()
: super(
...
dependencies: Dependencies<PageState>(
adapter: ToDoListAdapter(),
slots: <String, Dependent<PageState>>{
'report': ReportConnector() + ReportComponent()
}),
...
);
}
在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
类的定义:
class ReportConnector extends ConnOp<PageState, ReportState>
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函数:
Widget buildComponent(MixedStore<Object> store, Get<T> getter) {
final AbstractComponent<P> component = logic;
return component.buildComponent(store, () => connector.get(getter()));
}
这里的logic实际就是一个Component对象,在调用Component的buildComponent函数的
时候,使用get函数从一个大的父_state_中获取到当前Component需要的数据集。接下去,这个变换后的子_state_将被用在例如ViewBuilder或Redcuer函数中。
这是_connector_在数据获取上的作用。
set
还是在_Dependent类里面,看createSubReducer函数:
SubReducer<T> createSubReducer() {
final Reducer<P> reducer = logic.reducer;
return reducer != null ? connector.subReducer(reducer) : null;
}
首现从一个Logic(这里实际上是一个Component)对象中获取到外部设置进来的reducer,接着
调用subReducer返回一个SubReducer对象。SubReducer是一个被wrap后的Reducer
subReducer的实现在MutableConn中,ConnOp继承了MutableConn类,也获得了这个能
力。
SubReducer<T> subReducer(Reducer<P> reducer) {
return (T state, Action action, bool isStateCopied) {
final P props = get(state);
if (props == null) {
return state;
}
final P newProps = reducer(props, action);
final bool hasChanged = newProps != props;
final T copy = (hasChanged && !isStateCopied)
? _clone<T>(state) : state;
if (hasChanged) {
set(copy, newProps);
}
return copy;
};
}
它首现通过get函数得到一个变换后的数据集props,接着调用原始的reducer函数进行逻辑处
理,这里有一个优化也是SubReducer的作用,如果数据集在经过reducer处理之后发生了变化,
并且_state_已经被copy过一次了(isStateCopied==true),就直接把newProps通过set函数
更新到_state_中去。(这个优化可以防止多个子state发生变化的时候父state被拷贝多次)
至此,_connector_在数据更新上的作用也体现出来了。
ReportConnector
最后,就好理解ReportConnector的实现了:
class ReportConnector extends ConnOp<PageState, ReportState> {
@override
ReportState get(PageState state) {
final ReportState reportState = ReportState();
reportState.total = state.toDos.length;
reportState.done =
state.toDos.where((ToDoState tds) => tds.isDone).toList().length;
return reportState;
}
@override
void set(PageState state, ReportState subState) {}
}
很明显的,在get函数中,ReportState从PageState中获得了_total/done_字段。
总结
闲鱼客户端的详情页完全使用了_fish_redux_进行了重构,通过高内聚的Component+connector形式,使得Component可以被大量复用,很好得支持了5中类型的详情页。未来我们会基于_fish_redux_强大的扩展能力制作更多组件来满足不同业务对于框架的需求。
原文链接
本文为云栖社区原创内容,未经允许不得转载。


