让我们先抛开Flutter这个平台说话,如果让你实现数据共享,你能想到的基础方案有哪些。

  • 全局静态变量
  • 单例(XXXMnager,如UserManger)
  • 持久化(SharePref)

ok,以上方案真的是简单粗暴,好用到哭,然而,设计到数据数据变更之后及时通知到各个关注方就显得有点捉襟见肘了。

因此,因为有这样的一些需求存在,业界的一些大神(懒人)们通过不懈的努力,做出了一些惊天地,泣鬼神的全局数据共享通知方案。

那么,在flutter上,我比较关注的是,Redux,和event_bus 了,本文就是想来总结一下自己对Redux和event_bus的理解心得。

Redux

理解成本比较高,个人觉得耦合性也比较高,要搞清楚redux的原理,先要了解一下几个概念

Store

这里是他的构造函数,我们先不要管里面的一堆参数,看一眼就好。

Store(
    this.reducer, {
    State initialState,
    List> middleware = const [],
    bool syncStream: false,

    /// If set to true, the Store will not emit onChange events if the new State
    /// that is returned from your [reducer] in response to an Action is equal
    /// to the previous state.
    ///
    /// Under the hood, it will use the `==` method from your State class to
    /// determine whether or not the two States are equal.
    bool distinct: false,
  })

Store可以简单的理解为一个容纳各种数据以及对数据处理的action的一个仓库,可以看到可以给它配置一个泛型,这个泛型代表的就是下面的State,好,我们接着看State。

State

State实际上并不是Dart的基础类型,他其实就是上面Store定义中的那个S,对的,他就是一个泛型,他可以是dart基础类型String,int,double,也可以是你定义的class,都ok。总之一句话,他就是Store要守护的和维护的那个份数据。

StoreProvider

这里是他的构造函数,这里的参数比较简单,可以直接就了解一下

const StoreProvider({
    Key key,
    @required Store<Store> store,
    @required Widget child,
  })

一个store,一个child是一个Widget类型,所以理解起来是不是就是将这个store和child绑定起来的桥梁啊,嗯,牵线媒婆,store中的数据有变更可以通知到到child更新tree,那么具体child中的哪些个子child需要更新,是有谁决定的,当然是StoreConnector,媒婆把人给你放一起,你牵不牵手他可不管,谁管,肯定是StoreConnector啊,好,我们看StoreConnector。

StoreConnector

还是来看一下构造函数

StoreConnector({
    Key key,
    @required this.builder,
    @required this.converter,
    this.distinct = false,
    this.onInit,
    this.onDispose,
    this.rebuildOnChange = true,
    this.ignoreChange,
    this.onWillChange,
    this.onDidChange,
    this.onInitialBuild,
  });

这个构造函数的参数就有点多了,如果你感兴趣可以都了解一下,没时间的话,只需要了解required标记的。第一个是builder,这个就是WidgetBuilder,很明显,构建view用的,非常重点的converter这个参数,看一下converter的定义:

/// Convert the entire Store into a ViewModel. The ViewModel will be used

/// to build a Widget using the ViewModelBuilder.

typedef StoreConverter<S, ViewModel> = ViewModel Function(

  Store<Store> store,

);

看到之后也就没那么神秘了,就是将store转换为了ViewModel,转了之后,实际上就是可以更好的将数据交给builder去构建view,是吗?难道不是吗?我们前面提到了store可以接受到一个改变里面数据的action,那么这些action是谁给处理的呢?回过头来看Store的构造函数,里面的第一个参数是reducer,reducer的英文翻译为减速器,还原剂,反正就是听着挺别扭的,他不就是一个状态转换器嘛,数据有一个状态,经过action的处理,变成另外一个状态,是吗,这样你好理解了么?好吧,来看看Reducer。

Reducer

Reducer的定义如下:

typedef State Reducer<State>(State state, dynamic action);

一目了然,就是上面所说的状态应该action处理,变为另外一个状态,那么,state的处理仅仅只有Reducer处理,加入需要加入一些日志记录的,性能监控等处理,该怎么办呢?这种需求到处都有啊,大名鼎鼎的okhttp,处理一个http请求也可以说成是一个一系列的请求参数json请过action后端服务器的处理变为另外一串json,对么,那么对请求头,请求参数校验的一些处理,是不是都交给了拦截器interceptor?这点设计思路是想通的,因此这里的Middleware中间件虽然叫起来很神秘,但是他实际上就是拦截器,他在Reducer们之前执行,这点我了解到的是如此,有不同的见解的同学可以在评论中留下建议。

Middleware

定义如下

/// ### Example
///
///     loggingMiddleware(Store<int> store, action, NextDispatcher next) {
///       print('${new DateTime.now()}: $action');
///
///       next(action);
///     }
///
///     // Create your store with the loggingMiddleware
///     final store = new Store<int>(
///       counterReducer,
///       middleware: [loggingMiddleware],
///     );
typedef void Middleware<State>(
  Store<State> store,
  dynamic action,
  NextDispatcher next,
);

对,处理完之后,交给后面的action,理解起来也没什么成本了,好,那么总结一下我们将的这些概念,将他们串起来,用一副图来表达。

图1

用一句话来描述就是:

store通过storeProvider将自己给暴露出来,交给StoreConnector来更好链接到控件上,控件(也不一定需要在控件哪里,只不过我们好理解点)发送action交给store中的reducer处理,如果有中间件存在,那么中间件先拦截之后在交给recuder处理。处理之后store中的数据变更了,将会经有storeConnector通知组件更新。ok流程就这么跑完了。

event_bus

理解成本略低,耦合性也较低

// 初始化
import 'package:event_bus/event_bus.dart';
EventBus eventBus = new EventBus();
// 发送事件
eventBus.fire(event);
// 监听事件 
eventBus.on().listen((event) { 
    print(event.runtimeType);
});

也没有太多的概念,无耻的盗一幅图来说就是

图2

用一句话总结就是:对于某处所做的变更,如果想通知出去,那么交给总线吧,谁想关心谁问总线取。

总结

总体上来看,redux和bus都可以实现全局数据共享及变更通知,但是bus更加好理解概念也每有那么多,也不像redux需要通过storeConnector那么与控件绑定,造成不必要的耦合,个人倾向于使用bus解决全局数据共享变更通知的需求。