fish-redux解析

1.案例实现

阿里巴巴闲鱼出品的fish-redux,针对于大型互联网项目推荐使用的一种状态管理框架,今天来撸一撸,还是老样子实现如下一种双向绑定吧:


00947f39-e932-43d9-ac57-77e498aff72d.gif

1.1.安装插件FishReduxTemplate

使用fish-redux时候先安装下插件FishReduxTemplate,不然一步步写代码估计你要疯;
https://plugins.jetbrains.com/plugin/12139-fishreduxtemplate

1.2.添加依赖

dependencies:
  flutter:
    sdk: flutter
  fish_redux: ^0.3.3

1.3. 根据插件一键生成action/effect/page/reducer/state/view等代码;

生存方式直接参考https://plugins.jetbrains.com/plugin/12139-fishreduxtemplate这个插件的截图;

1.4 Demo代码实现

按照官方案例代码,需要配置一个PageRoutes,其内部pages是key/value,就是路由的意思,其他可以先不用配置;

Widget createApp() {
  final AbstractRoutes routes = PageRoutes(
    pages: >{
      'counter': CounterPage(),
    },
  );

  return MaterialApp(
    title: 'FishDemo',
    debugShowCheckedModeBanner: false,
    theme: ThemeData(
      primarySwatch: Colors.blue,
    ),
    home: routes.buildPage('counter', null),
    onGenerateRoute: (RouteSettings settings) {
      return MaterialPageRoute(builder: (BuildContext context) {
        return routes.buildPage(settings.name, settings.arguments);
      });
    },
  );
}
 
 

page.dart代码如下,有一个initState,effect,reducer,view,dependencies需要制定,其中effect是副作用的意思,而reducer是状态管理,view是视图管理,initState就是初始化state;

import 'package:fish_redux/fish_redux.dart';

import 'effect.dart';
import 'reducer.dart';
import 'state.dart';
import 'view.dart';

class CounterPage extends Page> {
  CounterPage()
      : super(
            initState: initState,
            effect: buildEffect(),
            reducer: buildReducer(),
            view: buildView,
            dependencies: Dependencies(
                adapter: null,
                slots: >{
                }),
            middleware: >[
            ],);

}

先定义一个CounterState的state类,其中必须实现clone,由于redux关键核心是状态state是不可变类型,状态更新是需要拷贝的,因此需要实现Cloneable接口,重写clone方法,达到状态更新,initState就是初始化时候数据,代码如下:

import 'package:fish_redux/fish_redux.dart';

class CounterState implements Cloneable {
  int count;

  @override
  CounterState clone() {
    return CounterState()
      ..count = count;
  }
}

CounterState initState(Map args) {
  CounterState state = CounterState();
  state.count = 10;
  return state;
}

然后定义CounterAction去管理Action的生成,这里有4个action,一开始想定义两个,但是发现同一个action只能被 effect或者reducer处理,如果同时定义了则effect先处理:

import 'package:fish_redux/fish_redux.dart';

//TODO replace with your own action
enum CounterAction {
  effect_increment,
  effect_decrement,
  reducer_increment,
  reducer_decrement
}


class CounterActionCreator {
  static Action onEffectIncrement() {
    return const Action(CounterAction.effect_increment);
  }

  static Action onReducerIncrement() {
    return const Action(CounterAction.reducer_increment);
  }

  static Action onEffectDecrement(){
    return const Action(CounterAction.effect_increment);
  }

  static Action onReducerDecrement(){
    return const Action(CounterAction.reducer_decrement);
  }
}

来看官方数据给的一张示意图,其中View触发事件是会先经过Effect,在调用reducer,之后生成新state,更新视图View;


image.png

effect/redux主要定义了2个action和对应的action处理方法,注意这里他们的action是不相同的,现在来看effect/redux代码:

import 'package:fish_redux/fish_redux.dart';
import 'action.dart';
import 'state.dart';

Effect buildEffect() {
  return combineEffects(>{
    CounterAction.effect_increment: _onIncrement,
    CounterAction.effect_decrement: _onDecrement,
  });
}

void _onIncrement(Action action, Context ctx) {
  print('Effect_onIncrement');
}

void _onDecrement(Action action, Context ctx) {
  print('Effect_onDecrement');
}
import 'package:fish_redux/fish_redux.dart';

import 'action.dart';
import 'state.dart';

Reducer buildReducer() {
  return asReducer(
    >{
      CounterAction.reducer_increment: _onIncrement,
      CounterAction.reducer_decrement: _onDecrement,
    },
  );
}

CounterState _onIncrement(CounterState state, Action action) {
  print('Reducer_onIncrement');
  state.count++;
  return state.clone();
}

CounterState _onDecrement(CounterState state, Action action) {
  print('Reducer_onDecrement');
  state.count--;
  return state.clone();
}

最后来看视图层代码,FloatingActionButton和ActioChip点击时候分别dispatch一个InCrement/Decrement的Action:

import 'package:fish_redux/fish_redux.dart';
import 'package:flutter/material.dart';

import 'action.dart';
import 'state.dart';

Widget buildView(
    CounterState state, Dispatch dispatch, ViewService viewService) {
  return Scaffold(
    appBar: AppBar(
      title: Text('Fish-Redux'),
    ),
    body: CounterHomeWapper(
        state: state, dispatch: dispatch, viewService: viewService),
    floatingActionButton: FloatingActionButtonWrapper(
        state: state, dispatch: dispatch, viewService: viewService),
  );
}

class FloatingActionButtonWrapper extends StatelessWidget {
  final CounterState state;
  final Dispatch dispatch;
  final ViewService viewService;

  const FloatingActionButtonWrapper(
      {Key key, this.state, this.dispatch, this.viewService})
      : super(key: key);

  @override
  Widget build(BuildContext context) {
    return FloatingActionButton(
      onPressed: (){
        dispatch(CounterActionCreator.onEffectIncrement());
        dispatch(CounterActionCreator.onReducerIncrement());
      },
      child: Text('${state.count}'),
    );
  }
}

class CounterHomeWapper extends StatelessWidget {
  final CounterState state;
  final Dispatch dispatch;
  final ViewService viewService;

  const CounterHomeWapper(
      {Key key, this.state, this.dispatch, this.viewService})
      : super(key: key);

  @override
  Widget build(BuildContext context) {
    return Center(
      child: ActionChip(
        label: Text('${state.count}'),
        onPressed: () {
          dispatch(CounterActionCreator.onEffectDecrement());
          dispatch(CounterActionCreator.onReducerDecrement());
        }
      ),
    );
  }
}

总体看起来确实做到数据分离,和flutter-redux比起来,没有需要自己state-->model方式,使用起来成本是代码量增加,换来的效益是逻辑解耦,界面清晰;

2.源码分析

2.1.界面加载流程

现在来分析下其代码流程吧,从加载界面起,在main方法有如下:

void main() => runApp(createApp());

而createApp返回的是一个

return MaterialApp(
    title: 'FishDemo',
    debugShowCheckedModeBanner: false,
    theme: ThemeData(
      primarySwatch: Colors.blue,
    ),
    home: routes.buildPage('counter', null),
    onGenerateRoute: (RouteSettings settings) {
      return MaterialPageRoute(builder: (BuildContext context) {
        return routes.buildPage(settings.name, settings.arguments);
      });
    },
  );
 
 

home界面加载的就是布局界面routes.buildPage:

/// Define a basic behavior of routes.
abstract class AbstractRoutes {
  Widget buildPage(String path, dynamic arguments);
}

@(知识体系总结)immutable
class PageRoutes implements AbstractRoutes {
  @override
  Widget buildPage(String path, dynamic arguments) =>
      pages[path]?.buildPage(arguments);
}

Page#buildPage方法会创建_PageWidget,然后被protectedWrapper包裹

 Widget buildPage(P param) => protectedWrapper(_PageWidget(
        page: this,
        param: param,
      ));

来看protectedWrapper

abstract class Component extends Logic implements AbstractComponent {
  final WidgetWrapper _wrapper;
  WidgetWrapper get protectedWrapper => _wrapper;
}

Component({
    ...
    WidgetWrapper wrapper,
  })  : assert(view != null),
        _wrapper = wrapper ?? _wrapperByDefault,
        ...

Page({
    ...
    WidgetWrapper wrapper,
    ...
  })  : super(
          ...
          wrapper: wrapper,
          ...
        );

之前案例代码是没有在CounterPage构造方法中并没有定义这个wrapper,那么protectedWrapper使用的是_wrapperByDefault,如下代码发现,其实_wrapperByDefault放回就是里面的Widget;

  static Widget _wrapperByDefault(Widget child) => child;

至此,可以看出Page#buildPage就是生存一个_PageWidget,_PageWidget是继承自StatefulWidget;

 Widget buildPage(P param) => protectedWrapper(_PageWidget(
        page: this,
        param: param,
      ));

class _PageWidget extends StatefulWidget {
  final Page page;
  final P param;

  const _PageWidget({
    Key key,
    @required this.page,
    @required this.param,
  }) : super(key: key);

  @override
  State createState() => _PageState();
}

class _PageState extends State<_PageWidget> {
   @override
  Widget build(BuildContext context) {
    ...
    //构建界面的地方
    return widget.page.buildComponent(
      _store,
      _store.getState,
      bus: _pageBus,
      enhancer: widget.page.enhancer,
    );
  } 
}

Component#buildComponent代码:

abstract class Component extends Logic implements AbstractComponent {
   @override
  Widget buildComponent(
    ...
  }) {
    ...
    return protectedWrapper(
      isPureView()
          ? _PureViewWidget(
              enhancer.viewEnhance(protectedView, this, store),
              getter,
              bus,
            )
          : ComponentWidget(
              component: this,
              getter: _asGetter(getter),
              store: store,
              key: key(getter()),
              bus: bus,
              enhancer: enhancer,
            ),
    );
  }
}

这里会判断isPureView,isPureView代码如下,

 bool isPureView() {
    return protectedReducer == null &&
        protectedEffect == null &&
        protectedDependencies == null;
  }

  Reducer get protectedReducer => _reducer;
  ReducerFilter get protectedFilter => _filter;
  Effect get protectedEffect => _effect;
  Dependencies get protectedDependencies => _dependencies;
  

一般情况下是指定了reducer,effect的,因此使用构建的ComponentWidget;

class ComponentWidget extends StatefulWidget {
   @override
   ComponentState createState() => component.createState(); 
}

class ComponentState extends State> {
  Widget build(BuildContext context) => _ctx.buildWidget();
}

class ComponentContext extends LogicContext implements ViewUpdater {
  @override
  Widget buildWidget() {
    //_widgetCache是缓存,一开始是null
    Widget result = _widgetCache;
    if (result == null) {
      //第一次进会执行,view(state, dispatch, this)方法
      result = _widgetCache = view(state, dispatch, this);

      dispatch(LifecycleCreator.build(name));
    }
    return result;
  }
}

view(state, dispatch, this)追踪到如下就是protectedView

final ViewBuilder view;

typedef ViewBuilder = Widget Function(
  T state,
  Dispatch dispatch,
  ViewService viewService,
);

 @override
  ComponentContext createContext(
   ...
  }) {
    assert(bus != null && enhancer != null);
    return ComponentContext(
      ...
      view: enhancer.viewEnhance(protectedView, this, store),
      ...
    );
  }

  ViewBuilder get protectedView => _view;  

其view就是page.dart中CounterPage构造方法中指定的buildView,至此界面从加载到显示过程告一段落;

2.2.Action分发流程

在ComponentContext#buildWidget时候,知道了view(state, dispatch, this)就是调用view.dart中的buildView方法,这里的dispatch是什么呢?

  @override
  Widget buildWidget() {
    Widget result = _widgetCache;
    if (result == null) {
      result = _widgetCache = view(state, dispatch, this);

      dispatch(LifecycleCreator.build(name));
    }
    return result;
  }

ComponentContext extends LogicContext,其dispatch是在LogicContext定义的;

abstract class LogicContext extends ContextSys with _ExtraMixin {
       /// create Dispatch
    _dispatch = logic.createDispatch(
      _effectDispatch,
      logic.createNextDispatch(
        this,
        enhancer,
      ),
      this,
    );
    
    @override
    dynamic dispatch(Action action) => _dispatch(action);
}

LogicContext#createDispatch调用的是父类 AbstractLogic#createDispatch,而实现类是Logic#createDispatch,代码如下

  @override
  Dispatch createDispatch(
    Dispatch effectDispatch,
    Dispatch nextDispatch,
    Context ctx,
  ) =>
      helper.createDispatch(effectDispatch, nextDispatch, ctx);

createDispatch代码如下:

/// return [Dispatch]
Dispatch createDispatch(Dispatch onEffect, Dispatch next, Context ctx) =>
    (Action action) {
      final Object result = onEffect?.call(action);
      if (result == null || result == false) {
        next(action);
      }

      return result == _SUB_EFFECT_RETURN_NULL ? null : result;
    };

这里判断了onEffect是否为空,如果不为空,需要先执行effect中处理Action的方法,之后根据result的值去看是否需要执行next(action);从这里可以得出一个结论:

1.effect,reducer两个都存在时候effect先执行;
2.effect,reducer两个都定义了相同的action时候,只有effect会执行,而reducer不会执行对应代码

接下来看看这个Dispatch onEffect, Dispatch next分别对应什么,这两个Dispatch都是从logic.createDispatch传过来的;

  /// create Dispatch
    _dispatch = logic.createDispatch(
      _effectDispatch,
      logic.createNextDispatch(
        this,
        enhancer,
      ),
      this,
    );

    _effectDispatch = logic.createEffectDispatch(this, enhancer);


  @override
  Dispatch createEffectDispatch(ContextSys ctx, Enhancer enhancer) {
    return helper.createEffectDispatch(

        /// enhance userEffect
        enhancer.effectEnhance(
          protectedEffect,
          this,
          ctx.store,
        ),
        ctx);
  }

/// return [EffectDispatch]
Dispatch createEffectDispatch(Effect userEffect, Context ctx) {
  return (Action action) {
    final Object result = userEffect?.call(action, ctx);

    //skip-lifecycle-actions
    if (action.type is Lifecycle && (result == null || result == false)) {
      return _SUB_EFFECT_RETURN_NULL;
    }

    return result;
  };
}

  @override
  Dispatch createNextDispatch(ContextSys ctx, Enhancer enhancer) =>
      helper.createNextDispatch(ctx);

  /// return [NextDispatch]
  Dispatch createNextDispatch(ContextSys ctx) => (Action action) {
      ctx.broadcastEffect(action);
      ctx.store.dispatch(action);
    };
 
 

如上代码一步步跟踪:
onEffect对应createEffectDispatch
next对应createNextDispatch

接下来分析createEffectDispatch代码

/// return [EffectDispatch]
Dispatch createEffectDispatch(Effect userEffect, Context ctx) {
  return (Action action) {
    //执行userEffect方法
    final Object result = userEffect?.call(action, ctx);

    //skip-lifecycle-actions
    if (action.type is Lifecycle && (result == null || result == false)) {
      return _SUB_EFFECT_RETURN_NULL;
    }

    return result;
  };
}

userEffect?.call(action, ctx)执行combineEffects响应的代码:

/// for action.type which override it's == operator
/// return [UserEffecr]
Effect combineEffects(Map> map) =>
    (map == null || map.isEmpty)
        ? null
        : (Action action, Context ctx) {
            //1.
            final SubEffect subEffect = map.entries
                .firstWhere(
                  (MapEntry> entry) =>
                      action.type == entry.key,
                  orElse: () => null,
                )
                ?.value;
            //2.
            if (subEffect != null) {
              return subEffect.call(action, ctx) ?? _SUB_EFFECT_RETURN_NULL;
            }

            //skip-lifecycle-actions
            if (action.type is Lifecycle) {
              return _SUB_EFFECT_RETURN_NULL;
            }

            /// no subEffect
            return null;
          };

标记1处中,这里面的map就是在effect.dart中定义的map,如之前定义了{CounterAction.effect_increment: _onIncrement,
CounterAction.effect_decrement: _onDecrement,},然后找到对应map的value,这value就是_onIncrement/_onDecrement方法
标记2处,就直接回调对应的逻辑方法,如果可以正常回调,返回的值就不是一个null或者_SUB_EFFECT_RETURN_NULL;

在回头看如下代码:

Dispatch createDispatch(Dispatch onEffect, Dispatch next, Context ctx) =>
    (Action action) {
      //1.
      final Object result = onEffect?.call(action);
      if (result == null || result == false) {
        //2
        next(action);
      }

      return result == _SUB_EFFECT_RETURN_NULL ? null : result;
    };

标记1处如果effect定义了action,则effect处理,result返回的不会是一个null,那么next就不会被执行;
标记2处如果effect没定义,则reducer处理,result是会返回一个null的,因此没有对应方法处理;

接下来分析next执行action对应的是createNextDispatch:

/// return [NextDispatch]
Dispatch createNextDispatch(ContextSys ctx) => (Action action) {
      ctx.broadcastEffect(action);
      //1
      ctx.store.dispatch(action);
    };

重点关系标记1处,其store创建代码如下:

Store _createStore(final T preloadedState, final Reducer reducer) {
  return Store()
    ..getState = (() => _state)
    ..dispatch = (Action action) {
       ...
       try {
        _isDispatching = true;
        //1
        _state = _reducer(_state, action);
      } finally {
        _isDispatching = false;
      }
      ...
}

重点标记1处,在创建dispatch时候,定义了方法(){},其中会回调_reducer方法,而这_reducer,就是这个asReducer,因此最后调用到如下代码:

Reducer asReducer(Map> map) => (map == null ||
        map.isEmpty)
    ? null
    : (T state, Action action) =>
        map.entries
            .firstWhere(
                (MapEntry> entry) =>
                    action.type == entry.key,
                orElse: () => null)
                //1
            ?.value(state, action) ??
        state;

重点标记1处,这里.value(state, action) 就触发了reducer.dart中调用的方法;

至此事件分发到effect和reducer逻辑告一段落;

2.3.状态共享实现方式

在之前版本上,状态管理更新state数据源是通过InheritedWidget,在之后版本是手动调用setState方法的;

@deprecated
class PageProvider extends InheritedWidget {}

在ComponentState#initState方法中有两个关键地方:

class ComponentState extends State> {
   @mustCallSuper
  @override
  void initState() {
    super.initState();

    /// init context
    _ctx = widget.component.createContext(
      widget.store,
      context,
      () => widget.getter(),
      //1
      markNeedsBuild: () {
        if (mounted) {
          setState(() {});
        }
      },
      bus: widget.bus,
      enhancer: widget.enhancer,
    );

    /// register store.subscribe
    //2
    _ctx.registerOnDisposed(widget.store.subscribe(() => _ctx.onNotify()));

    _ctx.onLifecycle(LifecycleCreator.initState());
  }
}

标记处1:设置markNeedsBuild回调方法设置setState;
标记处2:通过onNotify回调触发markNeedsBuild;

Store()
..subscribe = (_VoidCallback listener) {
      ...
      _listeners.add(listener);
      ...
    }

可以看到ComponentState#initState标记2处会将_ctx.onNotify()存在_listeners中,接下来看dispatch方法回调:

Store()
..dispatch = (Action action) {
      ...
      try {
        _isDispatching = true;
        _state = _reducer(_state, action);
      } finally {
        _isDispatching = false;
      }
      //1
      final List<_VoidCallback> _notifyListeners = _listeners.toList(
        growable: false,
      );
      for (_VoidCallback listener in _notifyListeners) {
        //2
        listener();
      }

      _notifyController.add(_state);
    }

标记处1:_listeners会转化成回调的_notifyListeners;
标记处2:遍历_notifyListeners方法去依次执行listener方法;

这个listener方法中必然有一个就是 _ctx.onNotify();因此在dispatch(Action)完成后,会触发onNotify方法,在看onNotify源码:

  //ComponentContext#onNotify
  @override
  void onNotify() {
    final T now = state;
    if (shouldUpdate(_latestState, now)) {
      _widgetCache = null;

      markNeedsBuild();

      _latestState = now;
    }
  }

其就是经过比较新旧的state,然后调用markNeedsBuild方法,而就是一开始在ComponentState#initState中传入的:

 markNeedsBuild: () {
        if (mounted) {
          setState(() {});
        }
      },

于是乎,在DisPatch(Action)后就自动更新数据到视图View上了,至此,state状态更新到视图View上逻辑也分析完毕;更多fish-redux功能还未尝试,后续会一步步解析其代码;

demo github:https://github.com/1036711153/fish_redux_demo

你可能感兴趣的:(fish-redux解析)