当前位置:网站首页>Analyzing mobx responsive refresh mechanism from source code

Analyzing mobx responsive refresh mechanism from source code

2022-06-12 18:19:00 InfoQ

Preface

MobX  Your design seems magical , I feel like I used a  Observer  After, you can automatically track the changes of state objects , Achieve responsive refresh . How is this realized ? Let's comb through the source code .

Observer  class

Observer  The class relationship diagram of class is shown in the figure below .

null
There are several key classes , Let's introduce one by one .

StatelessObserverWidget

abstract class StatelessObserverWidget extends StatelessWidget
 with ObserverWidgetMixin {
 /// Initializes [key], [context] and [name] for subclasses.
 const StatelessObserverWidget(
 {Key? key, ReactiveContext? context, String? name})
 : _name = name,
 _context = context,
 super(key: key);

 final String? _name;
 final ReactiveContext? _context;

 @override
 String getName() => _name ?? '$this';

 @override
 ReactiveContext getContext() => _context ?? super.getContext();

 @override
 StatelessObserverElement createElement() => StatelessObserverElement(this);
}

there  
createElement
  covers  
StatelessWidget
  Methods , Back to a
StatelessObserverElement
object , The purpose is to control
Element
Refresh of .

ObserverWidgetMixin

This is one for  
Widget
  Of
mixin
, The main purpose is to use  
createReaction
  Method creation  
reaction
, In order to call the corresponding method when the state changes . This  
createReaction
  It's actually in
ObserverElementMixin
Called in .

mixin ObserverWidgetMixin on Widget {
 String getName();

 ReactiveContext getContext() => mainContext;

 @visibleForTesting
 Reaction createReaction(
 Function() onInvalidate, {
 Function(Object, Reaction)? onError,
 }) =>
 ReactionImpl(
 getContext(),
 onInvalidate,
 name: getName(),
 onError: onError,
 );

 void log(String msg) {
 debugPrint(msg);
 }
}

StatelessObserverElement

StatelessObserverElement
This is actually a special  
StatelessElement
. This class is just mixed with
ObserverElementMixin
. All special businesses are in
ObserverElementMixin
Implemented in , Let's see
ObserverElementMixin
Source code .

mixin ObserverElementMixin on ComponentElement {
 ReactionImpl get reaction => _reaction;
 late ReactionImpl _reaction;

 // Not using the original `widget` getter as it would otherwise make the mixin
 // impossible to use
 ObserverWidgetMixin get _widget => widget as ObserverWidgetMixin;

 @override
 void mount(Element? parent, dynamic newSlot) {
 _reaction = _widget.createReaction(invalidate, onError: (e, _) {
 FlutterError.reportError(FlutterErrorDetails(
 library: 'flutter_mobx',
 exception: e,
 stack: e is Error ? e.stackTrace : null,
 ));
 }) as ReactionImpl;
 super.mount(parent, newSlot);
 }

 void invalidate() => markNeedsBuild();

 @override
 Widget build() {
 late Widget built;

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

 if (!reaction.hasObservables) {
 _widget.log(
 'No observables detected in the build method of ${reaction.name}',
 );
 }

 return built;
 }

 @override
 void unmount() {
 reaction.dispose();
 super.unmount();
 }
}

You can see , This  
mixin
  Reload the  
Elemennt
  Of  
mount
  Method , stay
mount
  It creates  
reaction
, The response method is
invalidate
, and  
invalidate
  The way is actually
markNeedsBuild
Method . That is, when the state data changes , In fact, it will pass through  
reaction
  To call
markNeedsBuild
notice
Element
Refresh , This method actually triggers  
Widget
  Of  
build
  Method .
In this  
mixin
  It's also overloaded  
build
Fang . Here we call
reaction
Of
track
Method . Follow it layer by layer , In fact, the main purpose here is to
observer
Objects and their dependencies ( In fact, it is
Observer
  Of  
builder
Back to
widget
) Binding .

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);

 // Computed = Observable + Derivation
 if (observable is Computed) {
 if (observable._dependenciesState.index >
 lowestNewDerivationState.index) {
 lowestNewDerivationState = observable._dependenciesState;
 }
 }
 }

 // Remove previous observables
 for (final ob in staleObservables) {
 ob._removeObserver(derivation);
 }

 if (lowestNewDerivationState != DerivationState.upToDate) {
 derivation
 .._dependenciesState = lowestNewDerivationState
 .._onBecomeStale();
 }

 derivation
 .._observables = derivation._newObservables!
 .._newObservables = {}; // No need for newObservables beyond this point
 }

This line is basically finished , How exactly did you track it ? Let's see  MobX  The generated part of the code .

State object tracking

In the generated code , with
@observable
The member generation code of the annotation is as follows :

final _$praiseCountAtom = Atom(name: 'ShareStoreBase.praiseCount');

@override
int get praiseCount {
 _$praiseCountAtom.reportRead();
 return super.praiseCount;
}

@override
set praiseCount(int value) {
 _$praiseCountAtom.reportWrite(value, super.praiseCount, () {
 super.praiseCount = value;
 });
}

The key here is  
get
  Method is called
Atom
Class
reportRead
Method . In fact, the final call is
_reportObserved
Method . This method is actually the previous
Observer
The dependencies of the binding are associated with the corresponding state object properties . therefore , Only when a property of the state object is updated , Only update components that depend on this property , Achieve accurate updates .

void _reportObserved(Atom atom) {
 final derivation = _state.trackingDerivation;

 if (derivation != null) {
 derivation._newObservables!.add(atom);
 if (!atom._isBeingObserved) {
 atom
 .._isBeingObserved = true
 .._notifyOnBecomeObserved();
 }
 }
}

Next let's look at  
set
  Method .
set
  Method actually changes the properties of the state object , Here we call
Atom
Class
reportWrite
Method . This triggers the following
reaction
Scheduling method :

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

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

This scheduling method will eventually execute
reaction
  Of
_run
  Method , Here we see the execution
_onInvalidate
  Method , This method is exactly in
ObserverElementMixin
in  
createReaction
When it came in , This method triggers  
Widget
  Of  
build
.

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();
}

From this, we know how to refresh the state object when it changes .

summary

Let's track the whole process , actual MobX  The way to complete the non perceptual response is as follows :

  • Controls the of rendering  
    Element
      yes
    StatelessObserverElement
    , In the class
    mount
      Stage through
    createReaction
    registered  
    reaction
    .
  • StatelessObserverElement
    stay  
    build
      In the method
    reaction
      and
    observable
    Binding .
  • stay  
    Observer
      When reading the properties of the state object in , Will call its  
    get
      Method , This method will match the state object property with the corresponding  
    Observer
    Components   Binding .
  • When the properties of the state object are  
    set
      When changing , Will be dispatched to the bound of this property
    reaction
    , perform
    _onInvalidate
    Method to refresh , Thus, a responsive non perceptual refresh is realized .

Of course, this is just our simple analysis , actual  MobX There are more implementation details , Interested students can also have an in-depth understanding of their design ideas .
null
原网站

版权声明
本文为[InfoQ]所创,转载请带上原文链接,感谢
https://yzsam.com/2022/163/202206121815452320.html