flutter_redux 的實現原理

雨三樓發表於2021-03-26

介紹

redux 有三個重要的組成部分,分別是:provider,store,reducer,其中reducer和store負責接收我們自定的action,處理並且返回新的state,然後重新整理介面。

而在應對複雜業務場景的情況下,常用的則是:combineReducers和Middleware來處理多業務場景以及複雜的非同步處理等。combineReducers可以封裝多個reducer,來實現action的細粒度控制,middleware則通過攔截action的方式達到優先於reducer執行的目的。

除此之外,和provider的selector類似的,flutter_redux還提供了distinct控制,來判斷是否真的需要重新整理檢視。

image.png

中介軟體的實現原理

常需要在其中呼叫next或者dispatch轉發,否則沒有意義,一些靜默行為除外。

combineReducers和Middleware 實現原理類似,都是通過實現並重寫Dart的call函式,是類的例項函式化,在其中判斷型別,進而呼叫入參函式。

  @override
  dynamic call(Store<State> store, dynamic action, NextDispatcher next) {
    if (action is Action) {
      return middleware(store, action, next);
    } else {
      return next(action);
    }
  }
複製程式碼
 @override
  State call(State state, dynamic action) {
    if (action is Action) {
      return reducer(state, action);
    }

    return state;
  }
複製程式碼

combineReducers

combineReducers的實現較為簡單,直接for in 迴圈遍歷並呼叫。

Reducer<State> combineReducers<State>(Iterable<Reducer<State>> reducers) {
  return (State state, dynamic action) {
    for (final reducer in reducers) {
      state = reducer(state, action);
    }
    return state;
  };
}
複製程式碼

Middleware中介軟體需要在reducer之前呼叫,可以看下store的dispath實現:


/// _dispatchers 的初始化
List<NextDispatcher> _createDispatchers(
  List<Middleware<State>> middleware,
  NextDispatcher reduceAndNotify,
) {
  /// 將reducer加入到dispatchers中 [reducer]
  final dispatchers = <NextDispatcher>[]..add(reduceAndNotify);

  // Convert each [Middleware] into a [NextDispatcher]
  /// 將中介軟體加入到dispatchers中 [reducer , middlewares ...]
  for (var nextMiddleware in middleware.reversed) {
    final next = dispatchers.last;

    dispatchers.add(
      (dynamic action) => nextMiddleware(this, action, next),
    );
  }
  /// 反轉陣列,將中介軟體放到最前邊 [middlewares ... , reducer]
  return dispatchers.reversed.toList();
}

/// store呼叫dispatch
dynamic dispatch(dynamic action) {
    return _dispatchers[0](action);
}

複製程式碼

可以看到每次store呼叫dispatch的時候,都會從_dispatchers的第一個元素開始呼叫,而middleware中介軟體是類似連結串列的資料結構,next 儲存著下一個的中介軟體呼叫。

通過原始碼可以得知,在中介軟體中,我們可以攔截相同Action的Reducer,並通過next呼叫reducer,甚至可以通過dispatch進行轉發。

distinct 的作用原理

StoreConnector中的distinct

redux是通過流(StreamBuilder)來控制檢視重新整理的,檢視通過呼叫store.dispatch(action),來觸發流,然而redux的流控制並不像bloc一樣直接暴露給我們使用,而是自己針對流做了一系列的控制

void _createStream() {
    _stream = widget.store.onChange
        .where(_ignoreChange)
        .map(_mapConverter)
        // Don't use `Stream.distinct` because it cannot capture the initial
        // ViewModel produced by the `converter`.
        .where(_whereDistinct)
        // After each ViewModel is emitted from the Stream, we update the
        // latestValue. Important: This must be done after all other optional
        // transformations, such as ignoreChange.
        .transform(StreamTransformer.fromHandlers(
            handleData: _handleChange, handleError: _handleError));
  }
複製程式碼
  • 首先是提供了ignoreChange,可以根據state,來控制是否忽略本次重新整理
  • 然後是提供converter返回vm
  • 如果distinct為true的化,比較新舊vm,判斷是否需要重新整理介面。否則強制重新整理。

store中的distinct

在構建store的時候,會發現,store也提供了一個distinct,它的實現作用更為簡單粗暴:

 NextDispatcher _createReduceAndNotify(bool distinct) {
    return (dynamic action) {
      final state = reducer(_state, action);

      if (distinct && state == _state) return;

      _state = state;
      _changeController.add(state);
    };
  }
複製程式碼

在呼叫reducer之後,直接判斷新舊state是否相等,來控制流的傳送。

這兩個distinct都有控制重新整理的作用,區別在於StoreConnector中的distinct可以通過converter來控制判斷粒度,來實現變數級的重新整理控制,而store中的distinct僅僅適用於函式級。

如何優化?

1.通過distinct的原始碼實現,不難發現,他們的控制都是通過比較state或者vm來實現的,我們可以通過過載"=="方法來進行更加靈活的重新整理控制。

2.redux通過流實現,預設是非同步流,可以通過更改syncStream來實現同步流。