Flutter【02】mobx原理

news2024/12/26 12:30:06

简介:

概念

MobX unidirectional flow

MobX 区分了以下几个应用中的概念:

State(状态)

状态 是驱动应用的数据。 通常有像待办事项列表这样的领域特定状态,还有像当前已选元素的视图状态。 记住,状态就像是有数据的excel表格。

Derivations(衍生)

任何 源自状态并且不会再有任何进一步的相互作用的东西就是衍生。 衍生以多种形式存在:

  • 用户界面
  • 衍生数据,比如剩下的待办事项的数量。
  • 后端集成,比如把变化发送到服务器端。

MobX 区分了两种类型的衍生:

  • Computed values(计算值) - 它们是永远可以使用纯函数(pure function)从当前可观察状态中衍生出的值。
  • Reactions(反应) - Reactions 是当状态改变时需要自动发生的副作用。需要有一个桥梁来连接命令式编程(imperative programming)和响应式编程(reactive programming)。或者说得更明确一些,它们最终都需要实现I / O 操作。

刚开始使用 MobX 时,人们倾向于频繁的使用 reactions。 黄金法则: 如果你想创建一个基于当前状态的值时,请使用 computed

回到excel表格这个比喻中来,公式是计算值的衍生。但对于用户来说,能看到屏幕给出的反应则需要部分重绘GUI。

Actions(动作)

动作 是任一一段可以改变状态的代码。用户事件、后端数据推送、预定事件、等等。 动作类似于用户在excel单元格中输入一个新的值。

在 MobX 中可以显式地定义动作,它可以帮你把代码组织的更清晰。 如果是在严格模式下使用 MobX的话,MobX 会强制只有在动作之中才可以修改状态。

原则

MobX 支持单向数据流,也就是动作改变状态,而状态的改变会更新所有受影响的视图

Action, State, View

状态改变时,所有衍生都会进行原子级的自动更新。因此永远不可能观察到中间值。

所有衍生默认都是同步更新。这意味着例如动作可以在改变状态之后直接可以安全地检查计算值。

计算值延迟更新的。任何不在使用状态的计算值将不会更新,直到需要它进行副作用(I / O)操作时。 如果视图不再使用,那么它会自动被垃圾回收。

所有的计算值都应该是纯净的。它们不应该用来改变状态

相关文章:

mobx官方文档:

https://cn.mobx.js org/refguide/action.html

https://mobx.netlify.app/api/reaction

原理:

https://takeroro.github.io/2020/06/30/mobX%20flutter%20%E6%95%B0%E6%8D%AE%E6%B5%81%E5%8A%A8/

https://juejin.cn/post/6844903860184563720

https://www.jianshu.com/p/a47d77f6371d

https://zhuanlan.zhihu.com/p/421675450

基本用法:

  1. 使用Observer包裹需要刷新的UI组件。

  2. 创建可观察的model

  3. 使用命令自动生成.g文件

    flutter pub run build_runner build
    

示例代码:

import 'package:flutter/material.dart';
import 'package:flutter_mobx/flutter_mobx.dart';

import 'counter.dart';

class CounterExample extends StatefulWidget {
  const CounterExample({Key? key}) : super(key: key);

  
  CounterExampleState createState() => CounterExampleState();
}

class CounterExampleState extends State<CounterExample> {
  final Counter counter = Counter();

  
  Widget build(BuildContext context) => Scaffold(
        appBar: AppBar(
          backgroundColor: Colors.blue,
          title: const Text('MobX Counter'),
        ),
        body: Center(
          child: Column(
            mainAxisAlignment: MainAxisAlignment.center,
            children: <Widget>[
              const Text(
                'You have pushed the button this many times:',
              ),
              Observer(
                  builder: (_) => Text(
                        '${counter.value}',
                        style: const TextStyle(fontSize: 40),
                      )),
            ],
          ),
        ),
        floatingActionButton: FloatingActionButton(
          onPressed: counter.increment,
          tooltip: 'Increment',
          child: const Icon(Icons.add),
        ),
      );
}
// GENERATED CODE - DO NOT MODIFY BY HAND

part of 'counter.dart';

// **************************************************************************
// StoreGenerator
// **************************************************************************

// ignore_for_file: non_constant_identifier_names, unnecessary_brace_in_string_interps, unnecessary_lambdas, prefer_expression_function_bodies, lines_longer_than_80_chars, avoid_as, avoid_annotating_with_dynamic

mixin _$Counter on _Counter, Store {
  final _$valueAtom = Atom(name: '_Counter.value');

  
  int get value {
    _$valueAtom.reportRead();
    return super.value;
  }

  
  set value(int value) {
    _$valueAtom.reportWrite(value, super.value, () {
      super.value = value;
    });
  }

  final _$_CounterActionController = ActionController(name: '_Counter');

  
  void increment() {
    final _$actionInfo =
        _$_CounterActionController.startAction(name: '_Counter.increment');
    try {
      return super.increment();
    } finally {
      _$_CounterActionController.endAction(_$actionInfo);
    }
  }

  
  String toString() {
    return '''
value: ${value}
    ''';
  }
}
import 'package:mobx/mobx.dart';

part 'counter.g.dart';

class Counter = _Counter with _$Counter;

abstract class _Counter with Store {
  
  int value = 0;

  
  void increment() {
    value++;
  }
}

依赖:

dependencies:
  flutter:
    sdk: flutter


  # The following adds the Cupertino Icons font to your application.
  # Use with the CupertinoIcons class for iOS style icons.
  cupertino_icons: ^1.0.2
  mobx: ^2.0.6+1
  flutter_mobx: ^2.0.4



dev_dependencies:
  flutter_test:
    sdk: flutter
  build_runner: ^2.1.0
  mobx_codegen: ^2.0.0

运行原理:

Observer组件初始化

首先父组件调用build,接着创建Observer组件,调用Observer组件的mountbuild

Observer继承关系

Observer组件继承自StatelessObservderWidget

class Observer extends StatelessObserverWidget

StatelessObserverWidget调用了createElement方法

abstract class StatelessObserverWidget extends StatelessWidget
    with ObserverWidgetMixin {
	...
  
  StatelessObserverElement createElement() => StatelessObserverElement(this);
}

StatelessObserverElement混入了ObserverElementMixin

class StatelessObserverElement extends StatelessElement
    with ObserverElementMixin {

ObserverElementMixin继承自ComponentElementComponentElement继承自Element

mount方法

ObserverElementMixin复写了Element类的mount方法

elementmount方法的作用:

将该元素添加到指定父节点的指定槽位的树中。当一个新创建的元素被添加到框架中时,框架调用这个函数

mount方法,在初始化的时候被调用。

mixin ObserverElementMixin on ComponentElement {
	...
  late ReactionImpl _reaction;
	...
  
  void mount(Element? parent, dynamic newSlot) {
    _reaction = _widget.createReaction(invalidate, onError: (e, _) {
      ···
    }) as ReactionImpl;
    super.mount(parent, newSlot);
  }

  void invalidate() => markNeedsBuild();
	...
}

ObserverElementMixin调用了_widget.createReaction方法,用于_reaction初始化:

mixin ObserverWidgetMixin on Widget {
	...
  ReactiveContext getContext() => mainContext;

  /// A convenience method used for testing.
  
  Reaction createReaction(
    Function() onInvalidate, {
    Function(Object, Reaction)? onError,
  }) =>
      ReactionImpl(
        getContext(),
        onInvalidate,
        name: getName(),
        onError: onError,
      );

	...
}

_reaction初始化时传入的invalidate如下,该方法是状态变换时widget的行为,就是重新绘制

void invalidate() => markNeedsBuild();
ReactionImpl

每个ObserverElementMixin都持有一个ReactionImpl 类型的_reaction

可以这么理解,_reaction等同于一个Observer组件

ReactionImpl类的继承关系如下:

Derivation类的代码如下:

abstract class Derivation {
  String get name;
  late Set<Atom> _observables;
  Set<Atom>? _newObservables;

  MobXCaughtException? _errorValue;
  MobXCaughtException? get errorValue;

  late DerivationState _dependenciesState;

  void _onBecomeStale();

  // ignore: unused_element
  void _suspend();
}

每个derivation类都有自己的name_observables监听者集合, _dependenciesState状态

我们来简单看下mobx里的几种状态(DerivationState

enum DerivationState {
  // 在运行之前或(在批处理之外并且没有被观察到)此时派生没有保存任何关于依赖树的数据
  notTracking,

  // 自上次计算以来没有改变浅依赖不会重新计算推导这就是使 mobx 快速的原因
  upToDate,

  // 一些深度依赖改变了,但是不知道浅依赖改变是否需要先检查 UP_TO_DATE 或 POSSIBLY_STALE 目前只有 Computed 会传播 	      			POSSIBLY_STALE
  possiblyStaleß,

  // 自上次计算以来,浅层依赖关系发生了变化,并且在下次需要时需要重新计算推导。
  stale
}
ReactiveContext

MobX 的主要Context。 所有反应性操作和观察都在发生在这个Context中。 单例。

主要的属性和方法如下:

class ReactiveContext {
  ReactiveContext({ReactiveConfig? config}) {
    this.config = config ?? ReactiveConfig.main;
  }

  late ReactiveConfig _config;

  _ReactiveState _state = _ReactiveState();

  void startBatch() {
    ···
  }

  void endBatch() {
    ···
  }

  Derivation? _startTracking(Derivation derivation) {
    ···
  }

  void _endTracking(Derivation currentDerivation, Derivation? prevDerivation) {
    ···
  }

  T? trackDerivation<T>(Derivation d, T Function() fn) {
    ···
  }

  void _reportObserved(Atom atom) {
    ···
  }

  void _bindDependencies(Derivation derivation) {
    ···
  }

  void runReactions() {
    ···
  }

  void _runReactionsInternal() {
    ···
  }

}
_ReactiveState

简单介绍一下context类里的state

class _ReactiveState {
  /// 当前批次深度。 这用于跟踪 `transaction` / `action` 的深度。
  /// 当批处理结束时,我们执行所有的 [pendingReactions]
  int batch = 0;

  /// 跟踪当前执行的Derivation(reactions or computeds)。
  /// 这里使用的 Observables 链接到这个Derivation。
  Derivation? trackingDerivation;

  /// Are we in middle of executing the [pendingReactions].
  bool isRunningReactions = false;
		
	···
}
build方法:

初始化时build调用了两次,调用super.build调用一次,接着自身又调用一次

mount方法执行完成之后,ObserverElementMixin重写的build方法被调用。

mixin ObserverElementMixin on ComponentElement {
	···
  
  Widget build() {
    late final Widget built;

    reaction.track(() {
      built = super.build();
    });
		···
    return built;
  }
  ···
}

ObserverElementMixinbuild方法,调用了ReactionImpltrack方法

ReactionImpl类里的track方法主要调用了ReactiveContext 里的三个方法

  void track(void Function() fn) {
    _context.startBatch();
		···
    _context.trackDerivation(this, fn);
    ···
    _context.endBatch();
  }
startBatch方法:
  void startBatch() {
    _state.batch++;
  }
trackDerivation方法:

trackDerivation方法有两个参数,一个是ReactionImpl对象的内存地址,一个是Widgetbuild函数。

T? trackDerivation<T>(Derivation d, T Function() fn) {
    final prevDerivation = _startTracking(d);
    T? result;
  	···
    result = fn();
  	···
    _endTracking(d, prevDerivation);
    return result;
  }

trackDerivation方法内部主要调用了两个方法_startTracking_endTracking

  1. _startTracking方法:

    _startTracking将当前ReactiveContext持有的 _ReactivState里的trackingDerivation设置为当前ReactionImpl

    ReactiveContext持有的 _ReactivState里的trackingDerivation个人理解就是记录当前执行任务的ReactionImpl

    首先_startTracking调用 _resetDerivationState

    Derivation? _startTracking(Derivation derivation) {
      final prevDerivation = _state.trackingDerivation;
      _state.trackingDerivation = derivation;
    
      _resetDerivationState(derivation);
      derivation._newObservables = {};
    
      return prevDerivation;
    }
    

    _resetDerivationState方法里更改了状态,将当前ReactionImpl_dependenciesState状态变为upToDate,将当前ReactionImpl_observables里的``Atom_lowestObserverState状态变为upToDate`

      void _resetDerivationState(Derivation d) {
        if (d._dependenciesState == DerivationState.upToDate) {
          return;
        }
    
        d._dependenciesState = DerivationState.upToDate;
        for (final obs in d._observables) {
          obs._lowestObserverState = DerivationState.upToDate;
        }
      }
    
  2. result = fn():

    result = fn()调用了fn()方法

    		() {
          built = super.build();
        }
    

    调用了父组件的build,给Observer组件的built方法赋值

    父组件build后就会调用CounterExampleState组件的build,而在CounterExampleState组件里使用了CounterBase的值,所以在这里就会走到CounterBase下的get value方法里

    
    int get value {
      _$valueAtom.reportRead();
      return super.value;
    }
    

    他将atom添加到ReactionImpl维护的_newObservables

    主要看一下第一个方法_reportObserved

      void _reportObserved(Atom atom) {
        final derivation = _state.trackingDerivation;
    
        if (derivation != null) {
          derivation._newObservables!.add(atom);
        }
      }
    

    可以这样理解,当前的element一旦使用了atom维护的value(会调用_$Counter.getValue),这一步就会把对应的atom加入到element下的ReactionImpl维护的_newObservables

    接着回到 _context.trackDerivation方法里,fn执行完成之后,执行 _endTracking

  3. _endTracking方法:

    _endTracking将当前ReactiveContext持有的 _ReactivState里的trackingDerivation设置为prevDerivation

    void _endTracking(Derivation currentDerivation, Derivation? prevDerivation) {
      _state.trackingDerivation = prevDerivation;
      _bindDependencies(currentDerivation);
    }
    

    _bindDependencies 代码如下, ReactionImpl类维护了两个 _observables集合

    将当前ReactionImpl加入到Atom类型的observable集合的每一个子元素Atom中,只保留活跃的Atom,删除旧的Atom

    void _bindDependencies(Derivation derivation) {
        final staleObservables =
            derivation._observables.difference(derivation._newObservables!);
        final newObservables =
            derivation._newObservables!.difference(derivation._observables);
        var lowestNewDerivationState = DerivationState.upToDate;
    
        // Add newly found observables
        for (final observable in newObservables) {
          observable._addObserver(derivation);
    			···
        }
        // Remove previous observables
        for (final ob in staleObservables) {
          ob._removeObserver(derivation);
        }
      
       	···
          
        derivation
          .._observables = derivation._newObservables!
          .._newObservables = {}; // No need for newObservables beyond this point
      }
    
endBatch方法:

endBatch会判断contextstate是否还有任务,当没有任务的时候,调用runReactions

void endBatch() {
  if (--_state.batch == 0) {
    runReactions();
    ···
}

如果还有待办batch,或者已经正在执行重绘就返回

void runReactions() {
  if (_state.batch > 0 || _state.isRunningReactions) {
    return;
  }

  _runReactionsInternal();
}

void _runReactionsInternal() {
  
	...
	
	//context里_state的待办Reactions集合,也就是需要刷新的Observer
  final allReactions = _state.pendingReactions;
  final remainingReactions = allReactions.toList(growable: false);
  //清空待办
  allReactions.clear();
  for (final reaction in remainingReactions) {
  //
      reaction._run();
  }
  //清空待办
  _state
    ..pendingReactions = []
    ..isRunningReactions = false;
}

run里主要执行_onInvalidate()

void _run() {
  
  _context.startBatch();

  _onInvalidate();

  _context.endBatch();
  
}

还记得之前在ReactionImpl类初始化时介绍的吗

_reaction初始化时传入的invalidate如下,该方法是状态变换时widget的行为,就是重新绘制

void invalidate() => markNeedsBuild();

完整的更改value过程:

在阅读完整过程之前,先简单的介绍一下action

part of '../core.dart';

class Action {
  /// Creates an action that encapsulates all the mutations happening on the
  /// observables.
  ///
  /// Wrapping mutations inside an action ensures the depending observers
  /// are only notified when the action completes. This is useful to silent the notifications
  /// when several observables are being changed together. You will want to run your
  /// reactions only when all the mutations complete. This also helps in keeping
  /// the state of your application consistent.
  ///
  /// You can give a debug-friendly [name] to identify the action.
  ///
  /// ```
  /// var x = Observable(10);
  /// var y = Observable(20);
  /// var total = Observable(0);
  ///
  /// autorun((){
  ///   print('x = ${x}, y = ${y}, total = ${total}');
  /// });
  ///
  /// var totalUp = Action((){
  ///   x.value++;
  ///   y.value++;
  ///
  ///   total.value = x.value + y.value;
  /// }, name: 'adder');
  /// ```
  /// Even though we are changing 3 observables (`x`, `y` and `total`), the [autorun()]
  /// is only executed once. This is the benefit of action. It batches up all the change
  /// notifications and propagates them only after the completion of the action. Actions
  /// can also be nested inside, in which case the change notification will propagate when
  /// the top-level action completes.
  factory Action(Function fn, {ReactiveContext? context, String? name}) =>
      Action._(context ?? mainContext, fn, name: name);

  Action._(ReactiveContext context, this._fn, {String? name})
      : _controller = ActionController(context: context, name: name);

  String get name => _controller.name;

  final ActionController _controller;
  final Function _fn;

  dynamic call([List args = const [], Map<String, dynamic>? namedArgs]) {
    final runInfo = _controller.startAction();

    try {
      // Invoke the actual function
      if (namedArgs == null) {
        return Function.apply(_fn, args);
      } else {
        // Convert to symbol-based named-args
        final namedSymbolArgs =
            namedArgs.map((key, value) => MapEntry(Symbol(key), value));
        return Function.apply(_fn, args, namedSymbolArgs);
      }
    } finally {
      _controller.endAction(runInfo);
    }
  }
}

/// `ActionController` is used to define the start/end boundaries of code which
/// should be wrapped inside an action. This ensures all observable mutations are neatly
/// encapsulated.
///
/// You would rarely need to use this directly. This is primarily meant for the **`mobx_codegen`** package.
///
class ActionController {
  ActionController({ReactiveContext? context, String? name})
      : this._(context ?? mainContext, name: name);

  ActionController._(this._context, {String? name})
      : name = name ?? _context.nameFor('Action');

  final ReactiveContext _context;
  final String name;

  ActionRunInfo startAction({String? name}) {
    final reportingName = name ?? this.name;
    _context.spyReport(ActionSpyEvent(name: reportingName));
    final startTime = _context.isSpyEnabled ? DateTime.now() : null;

    final prevDerivation = _context.startUntracked();
    _context.startBatch();
    final prevAllowStateChanges = _context.startAllowStateChanges(allow: true);

    return ActionRunInfo(
      prevDerivation: prevDerivation,
      prevAllowStateChanges: prevAllowStateChanges,
      name: reportingName,
      startTime: startTime,
    );
  }

  void endAction(ActionRunInfo info) {
    final duration = _context.isSpyEnabled
        ? DateTime.now().difference(info.startTime!)
        : Duration.zero;
    _context.spyReport(
      EndedSpyEvent(type: 'action', name: info.name, duration: duration),
    );

    // ignore: cascade_invocations
    _context
      ..endAllowStateChanges(allow: info.prevAllowStateChanges)
      ..endBatch()
      ..endUntracked(info.prevDerivation);
  }
}

class ActionRunInfo {
  ActionRunInfo({
    required this.name,
    this.startTime,
    this.prevDerivation,
    this.prevAllowStateChanges = true,
  });

  final Derivation? prevDerivation;
  final bool prevAllowStateChanges;
  final String name;
  final DateTime? startTime;
}

action,创建一个封装所有发生在可观察对象上的变化的action。在action中包装变化可确保仅在action完成时通知依赖的观察者。当多个可观察对象一起更改时,这对于使通知静音很有用。只有在所有变化完成后,您才会想要运行您的反应。这也有助于保持应用程序状态的一致性。

首先是点击事件increment(),可以看到开启了一个action

  
  void increment() {
    final _$actionInfo = _$CounterBaseActionController.startAction(
        name: 'CounterBase.increment');
    try {
      return super.increment();
    } finally {
      _$CounterBaseActionController.endAction(_$actionInfo);
    }
  }

startAction()主要调用了_context.startBatch();

ActionRunInfo startAction({String? name}) {
  final reportingName = name ?? this.name;
  _context.spyReport(ActionSpyEvent(name: reportingName));
  final startTime = _context.isSpyEnabled ? DateTime.now() : null;

  final prevDerivation = _context.startUntracked();
  _context.startBatch();
  final prevAllowStateChanges = _context.startAllowStateChanges(allow: true);

  return ActionRunInfo(
    prevDerivation: prevDerivation,
    prevAllowStateChanges: prevAllowStateChanges,
    name: reportingName,
    startTime: startTime,
  );
}

ReactiveContext下的startBatch,该stateContext持有的_ReactiveState

void startBatch() {
  _state.batch++;
}

接下来调用set value方法

set value(int value) {
  _$valueAtom.reportWrite(value, super.value, () {
    super.value = value;
  });
}

存值的时候调用了reportWrite方法

  void reportWrite<T>(T newValue, T oldValuße, void Function() setNewValue) {
    context.spyReport(ObservableValueSpyEvent(this,
        newValue: newValue, oldValue: oldValue, name: name));

    final actionName = context.isSpyEnabled ? '${name}_set' : name;

    // ignore: cascade_invocations
    context.conditionallyRunInAction(() {
      setNewValue();
      reportChanged();
    }, this, name: actionName);

    // ignore: cascade_invocations
    context.spyReport(EndedSpyEvent(type: 'observable', name: name));
  }

conditionallyRunInAction方法在第一次运行的时候isWithinBatchtrue,没有开启新的action而是直接开始执行fn()

void conditionallyRunInAction(void Function() fn, Atom atom,
    {String? name, ActionController? actionController}) {
  if (isWithinBatch) {
    enforceWritePolicy(atom);
    fn();
  } else {
    final controller = actionController ??
        ActionController(
            context: this, name: name ?? nameFor('conditionallyRunInAction'));
    final runInfo = controller.startAction();

    try {
      enforceWritePolicy(atom);
      fn();
    } finally {
      controller.endAction(runInfo);
    }
  }
}

执行fn()也就是 setNewValue()reportChanged(),调用reportChanged

  void reportChanged() {
    _context
      ..startBatch()
      ..propagateChanged(this)
      ..endBatch();
  }

首先是ReactiveContext下的startBatch,该stateContext持有的_ReactiveState

void startBatch() {
  _state.batch++;
}

接着调用propagateChanged方法,将所有订阅该atomelement下的ReactionImpl的状态都变为需要更新。

  void propagateChanged(Atom atom) {
    if (atom._lowestObserverState == DerivationState.stale) {
      return;
    }

    atom._lowestObserverState = DerivationState.stale;

    for (final observer in atom._observers) {
      if (observer._dependenciesState == DerivationState.upToDate) {
        observer._onBecomeStale();
      }
      observer._dependenciesState = DerivationState.stale;
    }
  }

observer._dependenciesState == DerivationState.upToDate为真时, 执行 onBecomeStale

void _onBecomeStale() {
  schedule();
}
void schedule() {
  if (_isScheduled) {
    return;
  }

  _isScheduled = true;
  _context
    ..addPendingReaction(this)
    ..runReactions();
}

在第一次调用时,batch的数量大于0,因为上面开启了一次action,调用了startBatch,并且reportChanged调用了startBatch

void runReactions() {
  if (_state.batch > 0 || _state.isRunningReactions) {
    return;
  }
  _runReactionsInternal();
}

然后来到endBatch(),endBatch首先将_state.batch进行--操作,代表着执行完了一个batch,只有所有batch都执行完成时,才会运行runReactions

先简单看下_ReactiveStatebatch

class _ReactiveState {
/// 当前批次深度。 这用于跟踪 `transaction` / `action` 的深度。
   /// 当批处理结束时,我们执行所有的 [pendingReactions]
  int batch = 0;
void endBatch() {
  if (--_state.batch == 0) {
    runReactions();

    for (var i = 0; i < _state.pendingUnobservations.length; i++) {
      final ob = _state.pendingUnobservations[i]
        .._isPendingUnobservation = false;

      if (ob._observers.isEmpty) {
        if (ob._isBeingObserved) {
          // if this observable had reactive observers, trigger the hooks
          ob
            .._isBeingObserved = false
            .._notifyOnBecomeUnobserved();
        }

        if (ob is Computed) {
          ob._suspend();
        }
      }
    }

    _state.pendingUnobservations = [];
  }
}

运行到这里内部的batch已经执行完了,接下来会执行外部actionendAction,再次贴一下代码


void increment() {
  final _$actionInfo = _$CounterBaseActionController.startAction(
      name: 'CounterBase.increment');
  try {
    return super.increment();
  } finally {
    _$CounterBaseActionController.endAction(_$actionInfo);//这里被调用了
  }
}

endAction代码如下

  void endAction(ActionRunInfo info) {
    final duration = _context.isSpyEnabled
        ? DateTime.now().difference(info.startTime!)
        : Duration.zero;
    _context.spyReport(
      EndedSpyEvent(type: 'action', name: info.name, duration: duration),
    );

    // ignore: cascade_invocations
    _context
      ..endAllowStateChanges(allow: info.prevAllowStateChanges)
      ..endBatch()
      ..endUntracked(info.prevDerivation);
  }

再次进入endBatch()

void endBatch() {
  if (--_state.batch == 0) {
    runReactions();

    for (var i = 0; i < _state.pendingUnobservations.length; i++) {
      final ob = _state.pendingUnobservations[i]
        .._isPendingUnobservation = false;

      if (ob._observers.isEmpty) {
        if (ob._isBeingObserved) {
          // if this observable had reactive observers, trigger the hooks
          ob
            .._isBeingObserved = false
            .._notifyOnBecomeUnobserved();
        }

        if (ob is Computed) {
          ob._suspend();
        }
      }
    }

    _state.pendingUnobservations = [];
  }
}

这次所有的batch执行完了,batch已经为0

void runReactions() {
  if (_state.batch > 0 || _state.isRunningReactions) {
    return;
  }

  _runReactionsInternal();
}

_runReactionsInternal主要执行 reaction. _run();

void _runReactionsInternal() {
  _state.isRunningReactions = true;

  var iterations = 0;
  final allReactions = _state.pendingReactions;

  // While running reactions, new reactions might be triggered.
  // Hence we work with two variables and check whether
  // we converge to no remaining reactions after a while.
  while (allReactions.isNotEmpty) {
    if (++iterations == config.maxIterations) {
      final failingReaction = allReactions[0];

      // Resetting ensures we have no bad-state left
      _resetState();

      throw MobXCyclicReactionException(
          "Reaction doesn't converge to a stable state after ${config.maxIterations} iterations. Probably there is a cycle in the reactive function: $failingReaction");
    }

    final remainingReactions = allReactions.toList(growable: false);
    allReactions.clear();
    for (final reaction in remainingReactions) {
      reaction._run();
    }
  }

  _state
    ..pendingReactions = []
    ..isRunningReactions = false;
}

run里主要执行_onInvalidate()

void _run() {
  if (_isDisposed) {
    return;
  }

  _context.startBatch();

  _isScheduled = false;

  if (_context._shouldCompute(this)) {
    try {
      _onInvalidate();
    } on Object catch (e, s) {
      // Note: "on Object" accounts for both Error and Exception
      _errorValue = MobXCaughtException(e, stackTrace: s);
      _reportException(_errorValue!);
    }
  }

  _context.endBatch();
}

创建Atom

完整代码:

class Atom {
  /// Creates a simple Atom for tracking its usage in a reactive context. This is useful when
  /// you don't need the value but instead a way of knowing when it becomes active and inactive
  /// in a reaction.
  ///
  /// Use the [onObserved] and [onUnobserved] handlers to know when the atom is active and inactive
  /// respectively. Use a debug [name] to identify easily.
  factory Atom(
          {String? name,
          Function()? onObserved,
          Function()? onUnobserved,
          ReactiveContext? context}) =>
      Atom._(context ?? mainContext,
          name: name, onObserved: onObserved, onUnobserved: onUnobserved);

  Atom._(this._context,
      {String? name, Function()? onObserved, Function()? onUnobserved})
      : name = name ?? _context.nameFor('Atom') {
    if (onObserved != null) {
      onBecomeObserved(onObserved);
    }

    if (onUnobserved != null) {
      onBecomeUnobserved(onUnobserved);
    }
  }

  final ReactiveContext _context;
  ReactiveContext get context => _context;

  final String name;

  // ignore: prefer_final_fields
  bool _isPendingUnobservation = false;

  DerivationState _lowestObserverState = DerivationState.notTracking;

  // ignore: prefer_final_fields
  bool _isBeingObserved = false;

  final Set<Derivation> _observers = {};

  bool get hasObservers => _observers.isNotEmpty;

  final Map<_ListenerKind, Set<void Function()>?> _observationListeners = {};

  void reportObserved() {
    _context._reportObserved(this);
  }

  void reportChanged() {
    _context
      ..startBatch()
      ..propagateChanged(this)
      ..endBatch();
  }

  void _addObserver(Derivation d) {
    _observers.add(d);

    if (_lowestObserverState.index > d._dependenciesState.index) {
      _lowestObserverState = d._dependenciesState;
    }
  }

  void _removeObserver(Derivation d) {
    _observers.remove(d);
    if (_observers.isEmpty) {
      _context._enqueueForUnobservation(this);
    }
  }

  void _notifyOnBecomeObserved() {
    final listeners = _observationListeners[_ListenerKind.onBecomeObserved];
    listeners?.forEach(_notifyListener);
  }

  static void _notifyListener(void Function() listener) => listener();

  void _notifyOnBecomeUnobserved() {
    final listeners = _observationListeners[_ListenerKind.onBecomeUnobserved];
    listeners?.forEach(_notifyListener);
  }

  void Function() onBecomeObserved(void Function() fn) =>
      _addListener(_ListenerKind.onBecomeObserved, fn);

  void Function() onBecomeUnobserved(void Function() fn) =>
      _addListener(_ListenerKind.onBecomeUnobserved, fn);

  void Function() _addListener(_ListenerKind kind, void Function() fn) {
    if (_observationListeners[kind] == null) {
      _observationListeners[kind] = {}..add(fn);
    } else {
      _observationListeners[kind]!.add(fn);
    }

    return () {
      final listeners = _observationListeners[kind];
      if (listeners == null) {
        return;
      }

      listeners.removeWhere((f) => f == fn);
      if (listeners.isEmpty) {
        _observationListeners[kind] = null;
      }
    };
  }
}

创建Atom

final _$valueAtom = Atom(name: 'CounterBase.value');

构造如下:

Atom._(context ?? mainContext,
    name: name, onObserved: onObserved, onUnobserved: onUnobserved);

mainContext定义如下:

final ReactiveContext mainContext = createContext(config: ReactiveConfig.main);

ReactiveContext createContext({ReactiveConfig? config}) =>
    ReactiveContext(config: config);

维护了一个_observersset,监听者合集。

 final Set<Derivation> _observers = {};

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2059087.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Ps:首选项 - 性能

Ps菜单&#xff1a;编辑/首选项 Edit/Preferences 快捷键&#xff1a;Ctrl K Photoshop 首选项中的“性能” Performance选项卡允许用户通过调整内存使用、GPU 设置、高速缓存设置以及多线程处理等选项&#xff0c;来优化 Photoshop 的性能。这对于处理大文件、复杂图像或需要…

Python 数据分析之Numpy学习(一)

Python 数据分析之Numpy学习&#xff08;一&#xff09; 一、Numpy的引入 1.1 矩阵/向量的按位运算 需求&#xff1a;矩阵的按位相加 [0,1,4] [0,1,8] [0,2,12] 1.1.1 利用python实现矩阵/向量的按位运算 # 1.通过列表实现 list1 [0, 1, 4] list2 [0, 1, 8]# 列表使用…

(17)ELK大型储存库的搭建

前言&#xff1a; els是大型数据储存体系&#xff0c;类似于一种分片式存储方式。elasticsearch有强大的查询功能&#xff0c;基于java开发的工具&#xff0c;结合logstash收集工具&#xff0c;收集数据。kibana图形化展示数据&#xff0c;可以很好在大量的消息中准确的找到符…

Marimo:下一代Python编程环境,颠覆传统Jupyter笔记本,自动化执行所有依赖代码块,告别繁琐手动操作

Marimo 是一个颠覆传统笔记本的全新编程环境&#xff0c;它以其反应式、交互式、可执行和可共享等特性&#xff0c;为开发者们带来前所未有的编程体验。Marimo 确保您的笔记本代码、输出和程序状态始终保持一致。它解决了传统笔记本&#xff08;如 Jupyter&#xff09;的许多问…

流媒体服务器如何让WebRTC支持H.265,同时又能支持Web js硬解码、软解码(MSE硬解、WASM软解)

为了这一整套的解决方案&#xff0c;调研研发整整花费了差不多半年多的时间&#xff0c;需达成的目标&#xff1a; 流媒体服务器端不需要将H.265转码成H.264&#xff0c;就能让Chrome解码播放H.265&#xff1b; 注意&#xff1a;现在很多市面上的软硬件通过转码H.265成H.264的…

CSP-CCF 202312-1 仓库规划

一、问题描述 二、解答 思路&#xff1a;定义二维数组&#xff0c;比较不同行的相同列数 代码如下&#xff1a; #include<iostream> using namespace std; int main() {int n, m;cin >> n >> m;int a[1001][11] { 0 };for (int i 1; i < n; i){for (…

贪心 + 分层图bfs,newcoder 76652/B

目录 一、题目 1、题目描述 2、输入输出 2.1输入 2.2输出 3、原题链接 二、解题报告 1、思路分析 2、复杂度 3、代码详解 一、题目 1、题目描述 2、输入输出 2.1输入 2.2输出 3、原题链接 https://ac.nowcoder.com/acm/contest/76652/B 二、解题报告 1、思路分析…

ELK基础搭建

一、认识ELK ELK是一套开源的日志分析系统&#xff0c;由elasticsearchlogstashKibana组成。 官网说明&#xff1a;https://www.elastic.co/cn/products 首先: 先一句话简单了解 E&#xff0c;L&#xff0c;K 这三个软件 elasticsearch: 分布式搜索引擎 logstash: 日志收集与…

领英(LinkedIn)公司主页创建方法分享

上次写了几篇关于领英注册的文章&#xff0c;也是有不少人加我&#xff0c;说有用。当然了也有还是不行的&#xff0c;还是不行的话一般都是比较复杂的问题&#xff0c;需要一些技术性的手段去解决。 然后最近也是有一些外贸朋友问公司主页注册创建的一些事情&#xff0c;今天的…

指挥调度平台——数字赋能,让出行更有温度

智慧交通指挥调度平台是基于信息技术和智能化系统的创新解决方案&#xff0c;旨在提升城市交通管理效率、改善交通流畅度、减少拥堵问题&#xff0c;以及增强城市交通运行的智能化水平。该平台整合了大数据分析、实时监测、智能优化算法等技术&#xff0c;为交通管理部门提供全…

虚拟现实技术的发展现状如何?

虚拟现实&#xff08;VR&#xff09;技术自2016年被广泛认为是元年之后&#xff0c;经历了快速增长和随后的调整期。目前&#xff0c;VR行业正处于快速发展期&#xff0c;技术不断进步&#xff0c;应用场景持续拓展。2024年VR技术发展现状概述&#xff1a; 1、行业发展阶段&am…

独家揭秘丨GreatSQL 的MDL锁策略升级对执行的影响

独家揭秘丨GreatSQL 的MDL锁策略升级对执行的影响 一、MDL锁策略介绍 GreatSQL 的MDL锁有个策略方法类MDL_lock_strategy&#xff0c;它根据对象的类型分为了scope类型和object类型&#xff0c;前者主要用于GLOBAL, COMMIT, TABLESPACE, BACKUP_LOCK and SCHEMA ,RESOURCE_GR…

基于tinymce实现多人在线实时协同文本编辑

基于tinymce实现多人在线实时协同文本编辑 前言 这可能是最后一次写tinymce相关的文章了&#xff0c;一方面tinymce的底层设计限制了很多功能的实现&#xff0c;另一方面tinymce本身越来越商业化&#xff0c;最新的7版本已经必须配置key&#xff0c;否则面临无法使用的问题。…

PPT如何添加水印?推荐两种方法!

在PPT演示文稿中添加水印&#xff0c;可以有效地保护版权或在背景上增加品牌标识。本文将介绍两种在PPT中添加水印的方法&#xff0c;帮助你轻松实现这一功能&#xff0c;一起来看看吧&#xff01; 方法一&#xff1a;在单张幻灯片上添加水印 1、选择目标幻灯片 打开PPT文件&…

防近视台灯有效果吗?学生家长们应该了解护眼台灯怎么选

在当前社会&#xff0c;近视的影响不容小视&#xff0c;除了对视觉健康的影响外&#xff0c;近视还可能对个人的心理健康产生负面影响。视力不佳可能导致自卑感和社会交往障碍&#xff0c;尤其是在儿童和青少年时期。保护视力健康要从小做起&#xff0c;家长们可以关注孩子的用…

【原创教程】电气电工07:网线的制作方法

电气电工经常会遇到做网线,我们做网线需要网线钳与测试仪。需要了解网线的两种接线标准。 我们来看一下网线钳的操作步骤: 这种压线钳也同时具有剥线、剪线功能。 用这种网线钳能制作RJ45网络线接头。RJ11电话线接头、4P电话线接头。适用于RJ45,RJ11型网线 做网线的时候我…

npm安装时一直在idealTree:npm: sill idealTree buildDeps卡住不动解决方法

npm安装xmysql时一直idealTree:npm: sill idealTree buildDeps卡住不动 问题解决&#xff0c;如下图所示 解决方法&#xff1a; 1、查看.npmrc位置&#xff0c;并去目录中删掉.npmrc文件 --在cmd&#xff08;DOS页面&#xff09;界面执行下述指令&#xff0c;可查看 .npmrc 文…

需要频繁查询的文档+索引命名规则

1.规则及样例 规则_S_00_R_240821.1_文档编号推荐命名规则V1.0.txt 模板_T_HV_C_248021.1_传感器及采集器IP分配表V1.0.xlsx 重要的需要频繁参考的文档纳入4段式文档编号体系&#xff1a;文档编号由四段组成&#xff1a; X_XX_X_XXXXXX.X T.........模板Template【其他还有&am…

自抗扰控制ADRC原理解析及案例应用

1. ADRC基本原理 1.1 ADRC的基本概念 自抗扰控制&#xff08;Active Disturbance Rejection Control&#xff0c;ADRC&#xff09;是一种先进的控制策略&#xff0c;由韩京清研究员于1998年提出。ADRC的核心思想是将系统内部和外部的不确定性因素视为总扰动&#xff0c;并通过…

AtCoder Beginner Contest 367 A~F

A.Shout Everyday&#xff08;枚举&#xff09; 题意&#xff1a; 在 A t C o d e r AtCoder AtCoder 王国&#xff0c;居民们每天都要在 A A A 点大声喊出他们对章鱼烧的热爱。 住在 A t C o d e r AtCoder AtCoder 王国的高桥每天 B B B 点睡觉&#xff0c; C C C 点…