零.前言
我們在使用fish-redux構建應用的時候,界面代碼(view)和事件的處理邏輯(reducer,effect)是完全解耦的,界面需要處理事件的時候將action分發給對應的事件處理邏輯去進行處理,而這個分發的過程就是下面要講的dispatch, 通過本篇的內容,你可以更深刻的理解一個action是如何一步步去進行分發的。
一.從example開始
為了更好的理解action的dispatch過程,我們就先以todo_list_page中一條todo條目的勾選事件為例,來看點擊後事件的傳遞過程,通過斷點debug我們很容易就能夠發現點擊時候發生的一切,具體過程如下:
- 用戶點擊勾選框,GestureDetector的onTap會被回調
- 通過buildView傳入的dispatch函數對doneAction進行分發,發現todo_component的effect中無法處理此doneAction,所以將其交給pageStore的dispatch繼續進行分發
- pageStore的dispatch會將action交給reducer進行處理,故doneAction對應的_markDone會被執行,對state進行clone,並修改clone後的state的狀態,然後將這個全新的state返回
- 然後pageStore的dispatch會通知所有的listeners,其中負責界面重繪的_viewUpdater發現state發生變化,通知界面進行重繪更新
二.Dispatch實現分析
Dispatch在實現的過程中借鑒了Elm。
Dispatch在fish-redux中的定義如下
typedef Dispatch = void Function(Action action);
本質上就是一個action的處理函數,接受一個action,然後對action進行分發。
下面我門通過源碼來進行詳細的分析
1.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); }; }; }; } }
上面分發的邏輯大概可以通過上圖來表示
- 通過onAction將action交給component對應的effect進行處理
- 當effect無法處理此action,且此action非lifecycle-actions,且不需中斷則廣播給當前Page的其餘所有effects
- 最後就是繼續將action分發給store的dispatch(parentDispatch傳入的其實就是store.dispatch)
2. 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的調用,處理完成後通知監聽者。
3.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的變化,這樣的擴展性給框架本身帶來無限可能。
作者:閑魚技術-盧克