美文网首页Flutter圈子Android开发Flutter 入门与实战
很流行的状态管理库 MobX 是怎么回事?

很流行的状态管理库 MobX 是怎么回事?

作者: 岛上码农 | 来源:发表于2022-03-03 20:44 被阅读0次

    本篇主要内容翻译自MobX 的官方 Readme 文档:https://github.com/mobxjs/mobx.dart

    MobX 简介

    MobX 是一个状态管理框架,它可以轻松地将应用的响应式数据和 UI 绑定起来。这个绑定是完全自动的,而且不会感觉到别扭。MobX 使得应用开发者只需要关注 UI 需要消费哪些响应式数据,而无需关注如何保持二者同步。

    MobX 的实现并没有什么神奇之处,但是使用了一些技巧来包裹消费(observables)的对象,哪里需要这些对象(reactions),并且自动跟踪这些对象。一旦observables对象发生了改变,所有 reactions 会被重新运行。有趣的是,这些reactions可以是来自例如控制台日志、UI 所需的网络接口数据等任何对象。

    注:MobX原先是JavaScript 的一个高效的状态管理库,Dart版本页试图带来同样的效果。关于 JavaScript 版本,可以到npm上查看:JavaScript版本MobX

    核心元素

    MobX 的核心要素有三个:、ObservablesActions、和 Reactions,如下图所示。接下来以简单的 Counter 计数器应用来介绍各个元素的使用。

    MobX 核心要素

    Observables

    Observables 代表着应用的响应式状态,它们可以是简单对象到复杂的对象树。如果定义应用的状态为一棵 observables 树,那么可以向消费状态的 UI(或应用中的其他观察者)暴露一棵响应式的状态树(reactive-state-tree)。对于计数器应用,可以通过下面的方式定义:

    import 'package:mobx/mobx.dart';
    
    final counter = Observable(0);
    

    对于复杂的observables,例如类,也是一样。

    class Counter {
      Counter() {
        increment = Action(_increment);
      }
    
      final _value = Observable(0);
      int get value => _value.value;
    
      set value(int newValue) => _value.value = newValue;
      Action increment;
    
      void _increment() {
        _value.value++;
      }
    }
    
    

    这些代码看起来有点重复。因此可以使用MobX 代码生成器来简化编写(*.g.dart文件会由代码生成器自动生成)。使用代码生成器,需要在开发依赖中加入builder_runnermobx_codegen 插件,并在代码目录下执行命令生成对应的*.g.dart 文件。

    flutter packages pub run build_runner build
    
    import 'package:mobx/mobx.dart';
    
    part 'counter.g.dart';
    
    class Counter = CounterBase with _$Counter;
    
    abstract class CounterBase with Store {
      @observable
      int value = 0;
    
      @action
      void increment() {
        value++;
      }
    }
    

    需要注意的是,需要使用注解来标记类的哪些属性是 observable 属性。这些注解是在 MobX 的代码生成器中定义的。如果想减少代码量,可以将@observable注解换成@readonly注解。更换后,私有属性将只提供 get 方法,而不会提供set方法。并且这样状态的使用者也无法更改他们的值。

    对于应用状态由核心状态和派生状态组成的情况,核心状态是指处理的业务领域固有的状态。例如,如果有个 Contact实体类,其中 firstNamelastName 组成了 Contact 的核心状态。然而,fullName 是一个派生状态,通过 firstNamelastName 组合得到。对于这种情况,依赖于核心状态或其他派生状态的称之为Computed Observables(有点类似 Vue 的计算属性)。这种属性也会在其依赖的状态对象改变时自动保持同步。这类派生的对象使用@computed 注解即可。

    import 'package:mobx/mobx.dart';
    
    part 'contact.g.dart';
    
    class Contact = ContactBase with _$Contact;
    
    abstract class ContactBase with Store {
      @observable
      String firstName;
    
      @observable
      String lastName;
    
      @computed
      String get fullName => '$firstName, $lastName';
    
    }
    

    Actions

    Actions 定义了如何改变 observables对象。相比直接更改,actions 让更改操作更有语义学的意义(更易于理解和维护)。例如,相比如直接使用 value++,调用一个 increment()动作将携带更多意义。除此之外,actions 能够分批次处理通知,以确保改变只有在完成之后才会被通知。从而使得观察者是基于 action 的完成这一原子操作通知的。
    注意,actions 是可以嵌套的,这种情况下,只有 最顶层的 action完成后才会发出通知。

    final counter = Observable(0);
    
    final increment = Action((){
      counter.value++;
    });
    

    在类里面使用 actions 的时候,可以利用注解@action 来简化代码。

    //...
    
    abstract class CounterBase with Store {
      //...
      
      @action
      void increment() {
        value++;
      }
    }
    

    对于异步操作,MobX 会自动处理,而无需使用 runInAction 来包裹。

    @observable
    String stuff = '';
    
    @observable
    loading = false;
    
    @action
    Future<void> loadStuff() async {
      loading = true; //This notifies observers
      stuff = await fetchStuff();
      loading = false; //This also notifies observers
    }
    

    Reactions

    Reactions是Mobx三元素的observablesactionsreactions的完结部分。Reactions是响应式系统中的观察者,一旦跟踪的 observable对象发生改变后就会被通知到。Reactions 有几种不同的方式。所有方式都会返回一个 ReactionDisposer 方法,调用该方法可以销毁该 reaction
    Reactions 的一个典型特性是自动跟踪所有的observable对象,而无需显式地与其绑定。在 reaction 中读取observables时就已经自动跟踪该对象了。

    • Reactions方式一,autorun 方法。
    import 'package:mobx/mobx.dart';
    
    String greeting = Observable('Hello World');
    
    final dispose = autorun((_){
      print(greeting.value);
    });
    
    greeting.value = 'Hello MobX';
    
    // Done with the autorun()
    dispose();
    
    
    // 打印结果:
    // Hello World
    // Hello MobX
    
    • Reactions 方式二,reaction方法:
    ReactionDisposer reaction<T>(T Function(Reaction) predicate, void Function(T) effect)
    

    predicate 方法中监测 observables 对象,然后当predicate返回不同的值时会执行 effect 方法。且只有 predicate 中的observables对象会被跟踪。

    import 'package:mobx/mobx.dart';
    
    String greeting = Observable('Hello World');
    
    final dispose = reaction((_) => greeting.value, (msg) => print(msg));
    
    greeting.value = 'Hello MobX'; // Cause a change
    
    // Done with the reaction()
    dispose();
    
    
    // 打印结果:
    // Hello MobX
    
    • Reactions 方式三,when 方法:
    ReactionDisposer when(bool Function(Reaction) predicate, void Function() effect)
    

    predicate方法返回 true 时才执行 effect 方法。当 effect 方法运行后,将会自动销毁。因此可以当作是一次性的 reaction。当然也可以提前手动销毁该 reaction

    import 'package:mobx/mobx.dart';
    
    String greeting = Observable('Hello World');
    
    final dispose = when((_) => greeting.value == 'Hello MobX', () => print('Someone greeted MobX'));
    
    greeting.value = 'Hello MobX'; // Causes a change, runs effect and disposes
    
    
    // Prints:
    // Someone greeted MobX
    
    • Reactions 方式四,Future 异步方法:
    Future<void> asyncWhen(bool Function(Reaction) predicate)
    

    和 when 方式类似,只是返回的结果是一个 Future 对象——在 predicate 方法返回 true 的时候完成。这对于等待 predicate 方法的返回值为 true 时很方便。

    final completed = Observable(false);
    
    void waitForCompletion() async {
      await asyncWhen(() => _completed.value == true);
    
      print('Completed');
    }
    

    Observer

    对于 app 而言,UI 是使用最多的可视化 reactions 之一。Observer 组件(在 flutter_mobx 插件中定义),在它的 build方法中,为 observables 对象提供了一个颗粒度可控的观察者。当observables发生改变的时候,Observer 将重建并重新绘制。

    总结

    简单的计数器代码已经上传至:MobX 状态管理源码。从官方的介绍可以看到,MobX 的使用方面还是挺简洁的,而且有了代码生成器加持后,状态管理部分的代码相对会容易编写很多。比如 Actions无需自己编写,状态属性也只需要加注解就行。对于无法生成的部分,其实可以使用 VSCode代码模板来完成,这样整个代码的编写效率就会很高了。

    相关文章

      网友评论

        本文标题:很流行的状态管理库 MobX 是怎么回事?

        本文链接:https://www.haomeiwen.com/subject/mkvurrtx.html