Flutter高内聚组件怎么做?阿里闲鱼打造开源高效方案!

简介: 作者:闲鱼技术-海潴 --fish_redux是闲鱼技术团队打造的flutter应用开发框架,旨在解决页面内组件间的高内聚、低耦合问题。开源地址:https://github.com/alibaba/fish-redux 从react_redux说起 redux对于前端的同学来说是一个比较熟悉的框架了,fish_redux借鉴了redux单项数据流思想。

作者:闲鱼技术-海潴

--
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_reduxreact_redux在抽象层面有很大的不同。

fish_redux本身是一个flutter上的应用框架,建立了自己的component体系,用来解决组件内的高内聚和组件间的低耦合。从connector角度来说,如何解决内聚问题,是设计中的重要考量。

fish_redux自己制造了Component树,Component聚合了statedispatch,每一个子Componentstate通过connector从父Componentstate中筛选。如图所示:

可以看到,fish_reduxconnector的主要作用把父子Component关联起来,最重要的操作是filterstate从上之下是一个严谨的树形结构,它的结构复用了Component的树形结构。类似一个漏斗形的数据管道,管理数据的分拆与组装。它表达了如何组装一个Component

而对于react_redux来说,它主要的作用在于把react框架和redux绑定起来,重点在于如何让React component具有Redux的功能。

从图中可以看到,react_reduxReact是平行的结构,经过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单元链接到一个更大的
ComponentAdapter上。这与我们之前的描述相符合。

connector到底是什么

知道了connector的基本作用,我们来看一下它到底链接了哪些东西以及如何链接。

先来看一下ReportConnector
类的定义:

class ReportConnector extends ConnOp<PageState, ReportState>

ReportConnector继承了ConnOp类,所有connector的操作包括+操作,都来自于ConnOp类。

connector类图

set/get

既然是数据管道,就会有获取放置

set函数的入参很好得表达了TP的意思,即把一个P类型的subState合并到T类型的
state中。

再回头看get函数,就很好理解了,get函数表达的就是如何从T类型的state中获取P类型
subStateDependent使用。

operator +

+操作符的重载是我们最初看到connector作用的地方,也是connector发挥作用的入口。

LogicComponentAdapter的父类,它表示页面组装元素的逻辑层,里面包含了
reducer/effect/higherEffect等与逻辑相关的元素以及它的组装过程。

operator+调用了createDependent函数,接着会调用到_Dependent类的构造函数,这里将
logicconnector放入_Dependent内部,在后面fish_reduxComponent组装的过程中,connector会随着外部对_Dependent中函数的调用发挥作用。

connector正式登场

铺垫了这么多,是该connector正式发挥作用的时候了。

get

我们以Component为例,会调用到_DependentbuildComponent函数:

Widget buildComponent(MixedStore<Object> store, Get<T> getter) {
    final AbstractComponent<P> component = logic;
    return component.buildComponent(store, () => connector.get(getter()));
}

这里的logic实际就是一个Component对象,在调用ComponentbuildComponent函数的
时候,使用get函数从一个大的父state中获取到当前Component需要的数据集。接下去,这个变换后的子state将被用在例如ViewBuilderRedcuer函数中。

这是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函数中,ReportStatePageState中获得了total/done字段。

总结

闲鱼客户端的详情页完全使用了fish_redux进行了重构,通过高内聚的Component+connector形式,使得Component可以被大量复用,很好得支持了5中类型的详情页。未来我们会基于fish_redux强大的扩展能力制作更多组件来满足不同业务对于框架的需求。

相关文章
|
4月前
Flutter 组件(二)文本 与 输入框组件
Flutter 组件(二)文本 与 输入框组件
74 0
|
4月前
|
容器
Flutter 组件(一)组件概述
Flutter 组件(一)组件概述
58 0
|
3天前
|
开发框架 前端开发 定位技术
【Flutter 前端技术开发专栏】Flutter 中的插件市场与开源资源利用
【4月更文挑战第30天】Flutter插件市场和开源资源加速开发进程。pub.dev是官方插件库,提供大量第三方插件,节约时间和保证质量。选择插件时关注功能需求、评价及维护状况。开源资源作为学习、解决问题和创新的平台,需注意版权、代码质量和兼容性。案例分析展示插件应用,开源社区促进交流与技术进步,未来市场将持续发展。善用资源,提升开发效率与项目竞争力。
【Flutter 前端技术开发专栏】Flutter 中的插件市场与开源资源利用
|
3天前
|
前端开发 搜索推荐 UED
【Flutter前端技术开发专栏】Flutter中的高级UI组件应用
【4月更文挑战第30天】探索Flutter的高级UI组件,如`TabBar`、`Drawer`、`BottomSheet`,提升应用体验和美观度。使用高级组件能节省开发时间,提供内置交互逻辑和优秀视觉效果。示例代码展示了如何实现底部导航栏、侧边导航和底部弹出菜单。同时,自定义组件允许个性化设计和功能扩展,但也带来性能优化和维护挑战。参考Flutter官方文档和教程,深入学习并有效利用这些组件。
【Flutter前端技术开发专栏】Flutter中的高级UI组件应用
|
3天前
|
存储 Dart 数据处理
Flutter是谷歌的开源移动框架,以其高性能和跨平台能力受开发者青睐。
【4月更文挑战第30天】Flutter是谷歌的开源移动框架,以其高性能和跨平台能力受开发者青睐。本文聚焦Flutter开发关键知识点:1) Dart语言和Flutter框架基础,如Widget和State;2) 路由管理,包括基本和命名路由,以及路由传值;3) 使用http、dio等库进行网络请求和数据处理;4) ThemeData定义应用主题,实现样式主题化。掌握这些技能将提升Flutter开发效率和应用质量。
|
9天前
|
Dart
Flutter 中优雅切换应用主题的组件
【Flutter】使用adaptive_theme组件优雅切换应用主题:封装MaterialApp,支持light/dark/system模式,自定义色彩,保存选择,含调试按钮。安装配置、设置主题、监听切换、自定义颜色、读取配置步骤详细。代码示例与学习路径推荐。[查看完整教程](https://flutter.ducafecat.com/blog/flutter-app-theme-switch)
Flutter 中优雅切换应用主题的组件
|
10天前
|
SQL 前端开发 JavaScript
IDM 平替 Gopeed Flutter 开源免费下载工具
IDM 替代品 Gopeed 是一个开源免费的 Flutter 下载工具,支持 HTTP、BitTorrent、Magnet 等协议。项目采用 getx 进行构建,已获得 13k Star。功能包括多平台下载、自定义下载目录和并发数、代理设置等。它还拥有浏览器扩展和各种下载插件。开发者可以参考代码学习 Flutter 和 getx。项目源码可在 GitHub 上找到,同时提供了编译和配置指南。Gopeed 是一个值得尝试的现代化下载工具,适用于 Flutter 开发者和用户。
IDM 平替 Gopeed Flutter 开源免费下载工具
|
8月前
|
人机交互
Flutter笔记 - ListTile组件及其应用
Flutter笔记 - ListTile组件及其应用
97 0
|
4月前
|
Linux 开发者 iOS开发
Flutter笔记:桌面端应用多窗口管理方案
Flutter笔记:桌面端应用多窗口管理方案
127 0
|
4月前
|
开发者 索引 容器
Flutter开发笔记:Flutter 布局相关组件
Flutter开发笔记:Flutter 布局相关组件
125 0