Flutter Redux 一些问题思考

目录:

  • 一、Redux 概述
  • 二、Redux 工作流程分析
    • 1.Action 发出后经历了什么?
    • 2.Middleware 是如何拦截 Action 的?
    • 3.如何更新 UI?
    • 4.distinct ?
    • 5.Store 中_createDispatchers 中为什么要倒序遍历?
    • ViewModel性能优化
  • 三、总结

一、Redux 概述

Redux 的概念是状态管理
Redux 的好处是:共享状态和单一数据。

image.png

Redux 的主要由三部分组成:StoreActionReducer

Action: 用于定义一个数据变化的请求行为。
Reducer:用于根据 Action 产生新状态,一般是一个方法。
Store: 用于存储和管理 state。

我们过下整个工作流程:

  • 用户(操作View)发出Action,发出方式就用到了dispatch方法;
  • Store自动调用Reducer,并且传入两个参数(当前State和收到的Action),Reducer会返回新的State,如果有MiddlewareStore会将当前State和收到的Action传递给MiddlewareMiddleware会调用Reducer 然后返回新的State
  • State一旦有变化,Store就会调用监听函数,来更新View

二、Redux 工作流程分析

1. Action 发出后经历了什么?

(1) 通过StoreProvider.of(context) 得到一个 store 对象发起一个 Action

StoreProvider.of<AppState>(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 做业务逻辑处理。
如果 falseAction 传递给下一个 Middleware or Reducer

class TypedMiddleware<State, Action> implements MiddlewareClass<State> {
  final void Function(
    Store<State> store,
    Action action,
    NextDispatcher next,
  ) middleware;

  TypedMiddleware(this.middleware);

  @override
  void call(Store<State> store, dynamic action, NextDispatcher next) {
    if (action is Action) {
      middleware(store, action, next);
    } else {
      next(action);
    }
  }
}

3. 如何更新 UI ?

(1) 通过 reducer 得到新的statestate 发送变化会通过 _changeController 发送 stateStoreConnector

class Store<State> {
  final StreamController<State> _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,与 StoreConnectordistinct 区别
    Stroe 中的 distinct 相当于全局的 distinct
    StoreConnector 中作用域仅局限于自己

5. Store 中 _createDispatchers 中为什么要倒序遍历

我们知道Action需要经过middleware才能到达reducer,所以 dispatchers 中数据结构必定是 middleware 在前。

  • 让数组中每一个元素持有下一个对象的引用,并保证 middlewarereducer 逻辑位置不发生改变
  /// 初始化   List<NextDispatcher> 对象
  List<NextDispatcher> _createDispatchers(
    List<Middleware<State>> middleware,
    NextDispatcher reduceAndNotify,
  ) {
    /// 将封装好 reduce 放入一个 List
    /// [()=> reducer()]
    final dispatchers = <NextDispatcher>[]..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(二)

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 203,772评论 6 477
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,458评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,610评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,640评论 1 276
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,657评论 5 365
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,590评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,962评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,631评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,870评论 1 297
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,611评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,704评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,386评论 4 319
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,969评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,944评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,179评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 44,742评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,440评论 2 342