美文网首页
Flutter状态管理之路(五)

Flutter状态管理之路(五)

作者: Owen_Lee | 来源:发表于2020-01-19 16:13 被阅读0次

    接上一篇Flutter状态管理之路(四)
    此篇主要介绍flutter_mobx

    Fish Redux

    版本:0.2.7

    库地址:https://github.com/alibaba/fish-redux/

    演进过程

    闲鱼fish_redux演进过程1.png 闲鱼fish_redux演进过程2.png

    概念

    对象 说明 所属库
    Action 表示一种意图,包含两个字段<br />type,payload
    Connector 表达了如何从一个大数据中读取小数据,<br />同时对小数据的修改如何同步给大数据,这样的数据连接关系
    Reducer 一个上下文无关的 pure function
    State 状态值
    Middleware 中间件,以AOP面向切面形式注入逻辑
    Component 对视图展现和逻辑功能的封装
    Effect 处理Action的副作用
    Dependent 表达了小组件|小适配器是如何连接到 大的Component 的
    Page 继承Component,针对页面级的抽象,内置一个Store(子Component共享)

    使用

    例子来源官方 Todos

    1. 入口路由配置

      /// 创建应用的根 Widget
      /// 1. 创建一个简单的路由,并注册页面
      /// 2. 对所需的页面进行和 AppStore 的连接
      /// 3. 对所需的页面进行 AOP 的增强
      Widget createApp() {
        final AbstractRoutes routes = PageRoutes(
          pages: <String, Page<Object, dynamic>>{
            /// 注册TodoList主页面
            'todo_list': ToDoListPage(),
      
          },
          visitor: (String path, Page<Object, dynamic> page) {
            /// 只有特定的范围的 Page 才需要建立和 AppStore 的连接关系
            /// 满足 Page<T> ,T 是 GlobalBaseState 的子类
            if (page.isTypeof<GlobalBaseState>()) {
              /// 建立 AppStore 驱动 PageStore 的单向数据连接
              /// 1. 参数1 AppStore
              /// 2. 参数2 当 AppStore.state 变化时, PageStore.state 该如何变化
              page.connectExtraStore<GlobalState>(GlobalStore.store,
                  (Object pagestate, GlobalState appState) {
                /// 根据appState变化pagestate
                return pagestate;
              });
            }
      
            /// AOP
            /// 页面可以有一些私有的 AOP 的增强, 但往往会有一些 AOP 是整个应用下,所有页面都会有的。
            /// 这些公共的通用 AOP ,通过遍历路由页面的形式统一加入。
            page.enhancer.append(
              ...
      
              /// Store AOP
              middleware: <Middleware<dynamic>>[
                logMiddleware<dynamic>(tag: page.runtimeType.toString()),
              ],
            );
          },
        );
      
        return MaterialApp(
          title: 'Fluro',
          home: routes.buildPage('todo_list', null),
          onGenerateRoute: (RouteSettings settings) {
            return MaterialPageRoute<Object>(builder: (BuildContext context) {
              return routes.buildPage(settings.name, settings.arguments);
            });
          },
        );
      }
      
    2. 新建Page

    class ToDoListPage extends Page<PageState, Map<String, dynamic>> {
      ToDoListPage()
          : super(
              initState: initState,
              effect: buildEffect(),
              reducer: buildReducer(),
              view: buildView,
            );
    }
    
    1. 定义state
    class PageState extends MutableSource
        implements GlobalBaseState, Cloneable<PageState> {
      List<ToDoState> toDos;
    
      @override
      Color themeColor;
    
      @override
      PageState clone() {
        return PageState()
          ..toDos = toDos
          ..themeColor = themeColor;
      }
    
      @override
      Object getItemData(int index) => toDos[index];
    
      @override
      String getItemType(int index) => 'toDo';
    
      @override
      int get itemCount => toDos?.length ?? 0;
    
      @override
      void setItemData(int index, Object data) => toDos[index] = data;
    }
    
    PageState initState(Map<String, dynamic> args) {
      //just demo, do nothing here...
      return PageState();
    }
    
    1. 定义Action
    enum PageAction { initToDos, onAdd }
    
    class PageActionCreator {
      static Action initToDosAction(List<ToDoState> toDos) {
        return Action(PageAction.initToDos, payload: toDos);
      }
    
      static Action onAddAction() {
        return const Action(PageAction.onAdd);
      }
    }
    
    1. 定义Reducer
    Reducer<PageState> buildReducer() {
      return asReducer(
        <Object, Reducer<PageState>>{PageAction.initToDos: _initToDosReducer},
      );
    }
    
    PageState _initToDosReducer(PageState state, Action action) {
      final List<ToDoState> toDos = action.payload ?? <ToDoState>[];
      final PageState newState = state.clone();
      newState.toDos = toDos;
      return newState;
    }
    
    
    1. 定义Effect
    Effect<PageState> buildEffect() {
      return combineEffects(<Object, Effect<PageState>>{
        Lifecycle.initState: _init,
        PageAction.onAdd: _onAdd,
      });
    }
    
    void _init(Action action, Context<PageState> ctx) {
      final List<ToDoState> initToDos = <ToDoState>[];
      /// 可作网络/IO等耗时操作
      ctx.dispatch(PageActionCreator.initToDosAction(initToDos));
    }
    
    void _onAdd(Action action, Context<PageState> ctx) {
      Navigator.of(ctx.context)
          .pushNamed('todo_edit', arguments: null)
          .then((dynamic toDo) {
        if (toDo != null &&
            (toDo.title?.isNotEmpty == true || toDo.desc?.isNotEmpty == true)) {
          ctx.dispatch(list_action.ToDoListActionCreator.add(toDo));
        }
      });
    }
    
    1. 定义View视图
    Widget buildView(PageState state, Dispatch dispatch, ViewService viewService) {
      return Scaffold(
        appBar: AppBar(
          backgroundColor: state.themeColor,  /// 获取state状态
          title: const Text('ToDoList'),
        ),
        body: Container(
          child: Column(
            children: <Widget>[
            
            ],
          ),
        ),
        floatingActionButton: FloatingActionButton(
          onPressed: () => dispatch(PageActionCreator.onAddAction()),  /// 发出意图改变状态
          tooltip: 'Add',
          child: const Icon(Icons.add),
        ),
      );
    }
    

    组装子Component

    1. 定义state
    class ReportState implements Cloneable<ReportState> {
      int total;
      int done;
    
      ReportState({this.total = 0, this.done = 0});
    
      @override
      ReportState clone() {
        return ReportState()
          ..total = total
          ..done = done;
      }
    
    }
    
    
    1. 定义Component
    class ReportComponent extends Component<ReportState> {
      ReportComponent()
          : super(
              view: buildView,
            );
    }
    
    1. 定义视图
    Widget buildView(
      ReportState state,
      Dispatch dispatch,
      ViewService viewService,
    ) {
      return Container(
          margin: const EdgeInsets.all(8.0),
          padding: const EdgeInsets.all(8.0),
          color: Colors.blue,
          child: Row(
            children: <Widget>[
              Container(
                child: const Icon(Icons.report),
                margin: const EdgeInsets.only(right: 8.0),
              ),
              Text(
                'Total ${state.total} tasks, ${state.done} done.',
                style: const TextStyle(fontSize: 18.0, color: Colors.white),
              )
            ],
          ));
    }
    
    1. 定义Connector来连接父子Component
    class ReportConnector extends ConnOp<PageState, ReportState>
        with ReselectMixin<PageState, ReportState> {
      @override
      ReportState computed(PageState state) {
        return ReportState()
          ..done = state.toDos.where((ToDoState tds) => tds.isDone).length
          ..total = state.toDos.length;
      }
    
      @override
      void set(PageState state, ReportState subState) {
        throw Exception('Unexcepted to set PageState from ReportState');
      }
    }
    
    1. page中使用,改造page如下
    class ToDoListPage extends Page<PageState, Map<String, dynamic>> {
      ToDoListPage()
          : super(
              initState: initState,
              effect: buildEffect(),
              reducer: buildReducer(),
              view: buildView,
              dependencies: Dependencies<PageState>(
                  slots: <String, Dependent<PageState>>{
                    'report': ReportConnector() + ReportComponent()
                  }),
            );
    }
    
    1. page的buildView改造如下
    Widget buildView(PageState state, Dispatch dispatch, ViewService viewService) {
      final ListAdapter adapter = viewService.buildAdapter();
      return Scaffold(
        appBar: AppBar(
          backgroundColor: state.themeColor,
          title: const Text('ToDoList'),
        ),
        body: Container(
          child: Column(
            children: <Widget>[
              viewService.buildComponent('report'),   /// 加载子Component
            ],
          ),
        ),
        floatingActionButton: FloatingActionButton(
          onPressed: () => dispatch(PageActionCreator.onAddAction()),
          tooltip: 'Add',
          child: const Icon(Icons.add),
        ),
      );
    }
    

    关键对象

    Middleware

    StoreMiddleware,实际是对Store的dispatch函数进行加强

    store_middleware.png
    /// fish-redux-master/lib/src/redux/apply_middleware.dart
    StoreEnhancer<T> applyMiddleware<T>(List<Middleware<T>> middleware) {
      return middleware == null || middleware.isEmpty
          ? null
          : (StoreCreator<T> creator) => (T initState, Reducer<T> reducer) {
                final Store<T> store = creator(initState, reducer);
                final Dispatch initialValue = store.dispatch;   /// 原始的dispatch
                store.dispatch = middleware
                    .map((Middleware<T> middleware) => middleware(   /// 执行middleware最外层函数返回Composable<T>,此函数在这里用于对dispatch包装
                          dispatch: (Action action) => store.dispatch(action),
                          getState: store.getState,
                        ))
                    .fold(
                      initialValue,
                      (Dispatch previousValue,
                              Dispatch Function(Dispatch) element) =>
                          element(previousValue),       /// 每次将上一个dispatch传入,返回一个新的dispatch,利用闭包,新的dispatch持有了上一个dispatch的引用
                    );
    
                return store;
              };
    }
    

    其中某一个Middleware示例如下:

    Middleware<T> logMiddleware<T>({
      String tag = 'redux',
      String Function(T) monitor,
    }) {
      return ({Dispatch dispatch, Get<T> getState}) {
        return (Dispatch next) {   /// 此方法在上一个示意代码段里,是fold方法里的element
          return isDebug()
              ? (Action action) {   /// 返回包装的Dispatch
                  print('---------- [$tag] ----------');
                  print('[$tag] ${action.type} ${action.payload}');
    
                  final T prevState = getState();
                  if (monitor != null) {
                    print('[$tag] prev-state: ${monitor(prevState)}');
                  }
    
                  next(action);
    
                  final T nextState = getState();
                  if (monitor != null) {
                    print('[$tag] next-state: ${monitor(nextState)}');
                  }
                }
              : next;
        };
      };
    }
    

    全局Store

    fish_redux_全局store时序图.png
     page.connectExtraStore<GlobalState>(GlobalStore.store,
                (Object pagestate, GlobalState appState) {
              final GlobalBaseState p = pagestate;
              if (p.themeColor != appState.themeColor) {
                if (pagestate is Cloneable) {
                  final Object copy = pagestate.clone();
                  final GlobalBaseState newState = copy;
                  newState.themeColor = appState.themeColor;
                  return newState;
                }
              }
              return pagestate;
            });
    

    Connector

    fish_redux_connector_reducer.png
    abstract class MutableConn<T, P> implements AbstractConnector<T, P> {
      const MutableConn();
    
      void set(T state, P subState);
    
      @override
      SubReducer<T> subReducer(Reducer<P> reducer) {
        /// 将本Component的reducer包装成新的reducer给父的store注入
        return (T state, Action action, bool isStateCopied) {
          final P props = get(state);
          if (props == null) {
            return state;
          }
          final P newProps = reducer(props, action);  /// 调用本Component的reducer,返回子的state
          final bool hasChanged = newProps != props;
          final T copy = (hasChanged && !isStateCopied) ? _clone<T>(state) : state;
          if (hasChanged) {
            set(copy, newProps);  /// 通知父Component同步状态
          }
          return copy;
        };
      }
    }
    

    其余详见官方文档:https://github.com/alibaba/fish-redux/blob/master/doc/README-cn.md

    总结

    优点:

    1. 每个Page一个Store,子Component共享其Store,单个Component仍拥有redux的特性以实现分治
    2. 子的reducer自动合并,与page的store自动进行数据同步
    3. 利用eventbus 建立page之间的联系,通过broadcast effect来分发page自身不关心的Action给其它page
    4. 可以全局共享状态,定义一个全局Store在用page.connectExtraStore关联

    缺点:

    1. 概念较多,学习曲线较高
    2. 需要定义的各类对象多、文件多
    3. 对项目规模把握不到位容易引入不必要的复杂度
    4. 代码结构侵入性较大

    未完待续

    fish_redux框架定义的概念很多,还需要继续深入...

    参考

    1. 手把手入门Fish-Redux开发flutter
    2. Connector的实现原理

    相关文章

      网友评论

          本文标题:Flutter状态管理之路(五)

          本文链接:https://www.haomeiwen.com/subject/spirzctx.html