目录:
- 一、Redux 概述
- 二、Redux 工作流程分析
- 1.Action 发出后经历了什么?
- 2.Middleware 是如何拦截 Action 的?
- 3.如何更新 UI?
- 4.distinct ?
- 5.Store 中_createDispatchers 中为什么要倒序遍历?
- ViewModel性能优化
- 三、总结
一、Redux 概述
Redux 的概念是状态管理
Redux 的好处是:共享状态和单一数据。
Redux 的主要由三部分组成:Store
、Action
、Reducer
。
Action
: 用于定义一个数据变化的请求行为。
Reducer:
用于根据 Action 产生新状态,一般是一个方法。
Store:
用于存储和管理 state。
我们过下整个工作流程:
- 用户(操作View)发出
Action
,发出方式就用到了dispatch
方法; -
Store
自动调用Reducer
,并且传入两个参数(当前State和收到的Action),Reducer
会返回新的State
,如果有Middleware
,Store
会将当前State
和收到的Action
传递给Middleware
,Middleware
会调用Reducer
然后返回新的State
; -
State
一旦有变化,Store
就会调用监听函数,来更新View
;
二、Redux 工作流程分析
1. Action 发出后经历了什么?
(1) 通过StoreProvider.of(context)
得到一个 store
对象发起一个 Action
。
StoreProvider.of(context, listen: listen).dispatch(action)
StoreProvider.of(context)
StoreProvider
是一个InheritedWidget
, 可以通过context
对象从叶子节点向上查找。从而获得父节点的_store
(2) 到 Store
源码, 调用了 dispatch()
,本质上调用了 Store
对象中 _dispatchers[0](action)
void dispatch(dynamic action) {
_dispatchers[0](action);
}
(3) 那么问题来了_dispatchers
到底是什么?
我们回到 Store
中的构造函数可以回答这个问题。
- 通过下面
_dispatchers
结构我们知道,_dispatchers[0](action)
调用的是Middleware1()
-
Middleware3()
中会检查是否消耗此action
,如果消耗则中断,否则执行Middleware2()
- 如果
Middleware
都不处理的话,将action
交给reducer(action)
Store
类初始化后_dispatchers
内部是如下结构:
_dispatchers
具有类似链表一样的性质,_dispatchers[n] 持有着 _dispatchers[n+1] 的引用 。意味着
_dispatchers[0] 可以调用 _dispatchers[1]_dispatchers =
[
(action
) => Middleware1(action
,_dispatchers[1]
),
(action
) => Middleware2(action
,_dispatchers[2]
),
(action
) => Middleware3(action
,_dispatchers[3]
),
(action
) => reducer(action
),
]
2. Middleware 是如何拦截 Action 的?
看源码我们很容易理解了,call()
中判断 Action
的类型,
如果 true
做业务逻辑处理。
如果 false
将 Action
传递给下一个 Middleware
or Reducer
class TypedMiddleware implements MiddlewareClass {
final void Function(
Store store,
Action action,
NextDispatcher next,
) middleware;
TypedMiddleware(this.middleware);
@override
void call(Store store, dynamic action, NextDispatcher next) {
if (action is Action) {
middleware(store, action, next);
} else {
next(action);
}
}
}
3. 如何更新 UI ?
(1) 通过 reducer
得到新的state
当 state
发送变化会通过 _changeController
发送 state
给 StoreConnector
class Store {
final StreamController _changeController;
NextDispatcher _createReduceAndNotify(bool distinct) {
return (dynamic action) {
final state = reducer(_state, action);
if (distinct && state == _state) return;
_state = state;
_changeController.add(state);
};
}
}
(2) StoreConnector
中内部订阅了 Store
中的 stream
。并保存旧的ViewModel
当心的ViewModel
到来时候与其比较是否更新。
-
where(_ignoreChange)
: 根据state
判断是否忽略此次更新UI -
map(_mapConverter)
: 将state
转换为ViewModel
, -
where(_whereDistinct)
:distinct = ture
时候,比较新旧ViewModel
相等时忽略此次更新UI -
_handleChange()
: 发出更新 UI 的事件,本质上还是通过SetState()
更新。
并调用onWillChange()
更新UI之前回调、onDidChange()
更新UI之后回调
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));
4. distinct ?
- ·distinct·的作用:
为true
时,新旧state
相同时不会进行 setState() -
Stroe
中的distinct
,与StoreConnector
中distinct
区别
Stroe
中的distinct
相当于全局的distinct
StoreConnector
中作用域仅局限于自己
5. Store 中 _createDispatchers 中为什么要倒序遍历
我们知道Action
需要经过middleware
才能到达reducer
,所以 dispatchers
中数据结构必定是 middleware
在前。
- 让数组中每一个元素持有下一个对象的引用,并保证
middleware
、reducer
逻辑位置不发生改变
/// 初始化 List 对象
List _createDispatchers(
List> middleware,
NextDispatcher reduceAndNotify,
) {
/// 将封装好 reduce 放入一个 List
/// [()=> reducer()]
final dispatchers = []..add(reduceAndNotify);
// 将 Middleware 转换为 NextDispatcher
/// [()=> reducer(),()=>Middleware3(), ()=>Middleware2() , ()=>Middleware1()]
for (var nextMiddleware in middleware.reversed) {
/// last 对象将 dispatchers 中的对象互相关联起来,形成责任链模式。
final next = dispatchers.last;
dispatchers.add(
(dynamic action) => nextMiddleware(this, action, next),
);
}
/// 翻转后如下结构
/// [()=>Middleware1(), ()=>Middleware2() , ()=>Middleware3(), ()=> reducer(),]
return dispatchers.reversed.toList();
}
6. ViewModel性能优化
我们的StoreConnector能够将store提取出信息并转化成ViewModel,这里其实是有一个性能优化的点的。
- 需要我们根据业务,在
ViewModel中
重写[==]
and[hashCode]
方法,然后把distinct
属性设为true
。
7. 项目 Middleware 中的 next 为何不使用?
三、总结
Redux 中源码并不是很多,以上问题是我自己提出并看源码,得出的结果,如果又不对的地方望指正。
Dart | 什么是Stream
Flutter | 状态管理探索篇——Redux(二)