flutter_redux 的实现原理
介绍
redux 有三个重要的组成部分,分别是:provider,store,reducer,其中reducer和store负责接收我们自定的action,处理并且返回新的state,然后刷新界面。
而在应对复杂业务场景的情况下,常用的则是:combineReducers和Middleware来处理多业务场景以及复杂的异步处理等。combineReducers可以封装多个reducer,来实现action的细粒度控制,middleware则通过拦截action的方式达到优先于reducer执行的目的。
除此之外,和provider的selector类似的,flutter_redux还提供了distinct控制,来判断是否真的需要刷新视图。
中间件的实现原理
常需要在其中调用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来实现同步流。
转载自:https://juejin.cn/post/6943869301698854925