開發

Fish Redux中的Dispatch是怎么實現的?

前言

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

我們在使用fish-redux構建應用的時候,界面代碼(view)和事件的處理邏輯(reducer,effect)是完全解耦的,界面需要處理事件的時候將action分發給對應的事件處理邏輯去進行處理,而這個分發的過程就是下面要講的dispatch, 通過本篇的內容,你可以更深刻的理解一個action是如何一步步去進行分發的。
從example開始

為了更好的理解action的dispatch過程,我們就先以todolistpage中一條todo條目的勾選事件為例,來看點擊后事件的傳遞過程,通過斷點debug我們很容易就能夠發現點擊時候發生的一切,具體過程如下:

  1. 用戶點擊勾選框,GestureDetector的onTap會被回調
  2. 通過buildView傳入的dispatch函數對doneAction進行分發,發現todo_component的effect中無法處理此doneAction,所以將其交給pageStore的dispatch繼續進行分發
  3. pageStore的dispatch會將action交給reducer進行處理,故doneAction對應的_markDone會被執行,對state進行clone,并修改clone后的state的狀態,然后將這個全新的state返回
  4. 然后pageStore的dispatch會通知所有的listeners,其中負責界面重繪的_viewUpdater發現state發生變化,通知界面進行重繪更新

Dispatch實現分析

Dispatch在fish-redux中的定義如下

typedef Dispatch = void Function(Action action);

本質上就是一個action的處理函數,接受一個action,然后對action進行分發。

下面我門通過源碼來進行詳細的分析。

01    component中的dispatch

buildView函數傳入的dispatch是對應的component的mainCtx中的dispatch, _mainCtx和componet的關系如下 component->ComponentWidget->ComponentState->_mainCtx->_dispatch而 _mainCtx的初始化則是通過componet的createContext方法來創建的,順著方法下去我們看到了dispatch的初始化

// redux_component/context.dart DefaultContext初始化方法
 DefaultContext({    @required this.factors,    @required this.store,    @required BuildContext buildContext,    @required this.getState,  })  : assert(factors != null),        assert(store != null),        assert(buildContext != null),        assert(getState != null),        _buildContext = buildContext {    final OnAction onAction = factors.createHandlerOnAction(this);
   /// create Dispatch    _dispatch = factors.createDispatch(onAction, this, store.dispatch);
   /// Register inter-component broadcast    _onBroadcast =        factors.createHandlerOnBroadcast(onAction, this, store.dispatch);    registerOnDisposed(store.registerReceiver(_onBroadcast));  }

context中的dispatch是通過factors來進行創建的,factors其實就是當前component,factors創建dispatch的時候傳入了onAction函數,以及context自己和store的dispatch。onAction主要是進行Effect處理。這邊還可以看到,進行context初始化的最后,還將自己的onAction包裝注冊到store的廣播中去,這樣就可以接收到別人發出的action廣播。

Component繼承自Logic

// redux_component/logic.dart
 @override  Dispatch createDispatch(      OnAction onAction, Context<T> ctx, Dispatch parentDispatch) {    Dispatch dispatch = (Action action) {      throw Exception(          'Dispatching while appending your effect & onError to dispatch is not allowed.');    };
   /// attach to store.dispatch    dispatch = _applyOnAction<T>(onAction, ctx)(      dispatch: (Action action) => dispatch(action),      getState: () => ctx.state,    )(parentDispatch);    return dispatch;  }
   static Middleware<T> _applyOnAction<T>(OnAction onAction, Context<T> ctx) {    return ({Dispatch dispatch, Get<T> getState}) {      return (Dispatch next) {        return (Action action) {          final Object result = onAction?.call(action);          if (result != null && result != false) {            return;          }
         //skip-lifecycle-actions          if (action.type is Lifecycle) {            return;          }

         if (!shouldBeInterruptedBeforeReducer(action)) {            ctx.pageBroadcast(action);          }
         next(action);        };      };    };  }}

上面分發的邏輯大概可以通過上圖來表示

  1. 通過onAction將action交給component對應的effect進行處理
  2. 當effect無法處理此action,且此action非lifecycle-actions,且不需中斷則廣播給當前Page的其余所有effects
  3. 最后就是繼續將action分發給store的dispatch(parentDispatch傳入的其實就是store.dispatch)

02   store中的dispatch

從store的創建代碼我們可以看到store的dispatch的具體邏輯

// redux/create_store.dart
 final Dispatch dispatch = (Action action) {    _throwIfNot(action != null, 'Expected the action to be non-null value.');    _throwIfNot(        action.type != null, 'Expected the action.type to be non-null value.');    _throwIfNot(!isDispatching, 'Reducers may not dispatch actions.');
   try {      isDispatching = true;      state = reducer(state, action);    } finally {      isDispatching = false;    }
   final List<_VoidCallback> _notifyListeners = listeners.toList(      growable: false,    );    for (_VoidCallback listener in _notifyListeners) {      listener();    }
   notifyController.add(state);  };

store的dispatch過程比較簡單,主要就是進行reducer的調用,處理完成后通知監聽者。

03   middleware

Page繼承自Component,增加了middleware機制,fish-redux的redux部分本身其實就對middleware做了支持,可以通過StoreEnhancer的方式將middlewares進行組裝,合并到Store的dispatch函數中。

middleware機制可以允許我們通過中間件的方式對redux的state做AOP處理,比如fish-redux自帶的logMiddleware,可以對state的變化進行log,分別打印出state變化前和變化后的值。

當Page配置了middleware之后,在創建pageStore的過程中會將配置的middleware傳入,傳入之后會對store的dispath進行增強加工,將middleware的處理函數串聯到dispatch中。

// redux_component/component.dart
 Widget buildPage(P param) {    return wrapper(_PageWidget<T>(      component: this,      storeBuilder: () => createPageStore<T>(            initState(param),            reducer,            applyMiddleware<T>(buildMiddleware(middleware)),          ),    ));  }
// redux_component/page_store.dart
PageStore<T> createPageStore<T>(T preloadedState, Reducer<T> reducer,        [StoreEnhancer<T> enhancer]) =>    _PageStore<T>(createStore(preloadedState, reducer, enhancer));
// redux/create_store.dart
Store<T> createStore<T>(T preloadedState, Reducer<T> reducer,        [StoreEnhancer<T> enhancer]) =>    enhancer != null        ? enhancer(_createStore)(preloadedState, reducer)        : _createStore(preloadedState, reducer);

所以這里可以看到,當傳入enhancer時,createStore的工作被enhancer代理了,會返回一個經過enhancer處理過的store。而PageStore創建的時候傳入的是中間件的enhancer。

// 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) {            assert(middleware != null && middleware.isNotEmpty);
           final Store<T> store = creator(initState, reducer);            final Dispatch initialValue = store.dispatch;            store.dispatch = (Action action) {              throw Exception(                  'Dispatching while constructing your middleware is not allowed. '                  'Other middleware would not be applied to this dispatch.');            };            store.dispatch = middleware                .map((Middleware<T> middleware) => middleware(                      dispatch: (Action action) => store.dispatch(action),                      getState: store.getState,                    ))                .fold(                  initialValue,                  (Dispatch previousValue,                          Dispatch Function(Dispatch) element) =>                      element(previousValue),                );
           return store;          };}

這里的邏輯其實就是將所有的middleware的處理函數都串到store的dispatch,這樣當store進行dispatch的時候所有的中間件的處理函數也會被調用。下面為各個處理函數的執行順序,

首先還是component中的dispatch?D1?會被執行,然后傳遞給store的dispatch,而此時store的dispatch已經經過中間件的增強,所以會執行中間件的處理函數,最終store的原始dispatch函數D2會被執行。

總結

通過上面的內容,現在我們可以知道一個action是如何一步步的派送給effect,reducer去進行處理的,我們也可以通過middleware的方式去跟蹤state的變化,這樣的擴展性給框架本身帶來無限可能。

我還沒有學會寫個人說明!

畢馬威報告:釘釘活躍用戶數超過2到10名之和

上一篇

Eclipse發布:2019年物聯網開發者調查

下一篇

你也可能喜歡

Fish Redux中的Dispatch是怎么實現的?

長按儲存圖像,分享給朋友

ITPUB 每周精要將以郵件的形式發放至您的郵箱


微信掃一掃

微信掃一掃
海天娱乐群 下期平码如何计算出来 刮刮乐拖把 上海麻将连连看单机手机版 浙江6十1开奖结果查询 上证指数今日 平特一肖研究方法 乐游广西棋牌柳州麻将 广东好彩1那里可以买 陕西11选五形态统计百宝彩 网上打鱼输钱经历