mobx.dart
Dart 语言的 MobX。轻松、响应式地管理您的 Dart 和 Flutter 应用程序的状态。
通过透明函数响应式编程 (TFRP),为您的 Dart 应用中的状态管理赋能。
MobX 是一个状态管理库,它可以简化应用程序的响应式数据与 UI 的连接。这种连接是完全自动的,感觉非常自然。作为应用程序开发者,您只需纯粹关注 UI(或其他地方)需要消费哪些响应式数据,而无需担心两者之间的同步问题。
这并非魔法,但它确实很智能,能够识别哪些数据正在被消费(可观察对象)以及在哪里被消费(反应),并自动为您进行跟踪。当可观察对象发生变化时,所有的反应都会重新运行。有趣的是,这些反应可以是任何东西,从简单的控制台日志、网络调用到重新渲染 UI。
MobX 对于 JavaScript 应用来说一直是一个非常高效的库,这个 Dart 语言的移植版本旨在带来同样水平的生产力。
核心概念

MobX 的核心是三个重要概念:可观察对象 (Observables)、动作 (Actions) 和 反应 (Reactions)。
可观察对象
可观察对象代表了您应用程序的响应式状态。它们可以是简单的标量,也可以是复杂的对象树。通过
将应用程序的状态定义为一个可观察对象的树,您可以暴露一个供 UI
(或应用中其他观察者)消费的*响应式状态树*。
一个简单的响应式计数器由以下可观察对象表示:
import 'package:mobx/mobx.dart';
final counter = Observable(0);
更复杂的可观察对象,如类,也可以被创建。
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_codegen,它允许您将上面的代码替换为以下内容:
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++;
}
}
请注意,这里使用了注解来标记类的可观察属性。是的,这里有一些头部的样板代码,
但对于任何类来说,这都是固定的。当您构建更复杂的类时,这些样板代码
就会显得微不足道,您将主要关注花括号内的代码。
注意:注解可通过 mobx_codegen 包获得。
只读
如果您想减少代码量,可以将 @observable 替换为 @readonly。
它会为每个私有变量生成一个公共的 getter,这样您的 store 的客户端
就无法更改其值。在这里阅读更多相关信息:链接
计算可观察对象
可以派生的东西,就应该自动派生。
您应用程序的状态由*核心状态*和*派生状态*组成。*核心状态*是您所处理的领域固有的状态。例如,如果您有一个 `Contact` 实体,`firstName` 和 `lastName` 就构成了 `Contact` 的*核心状态*。然而,`fullName` 是*派生状态*,是通过组合 `firstName` 和 `lastName` 得到的。
这种依赖于*核心状态*或*其他派生状态*的*派生状态*,被称为计算可观察对象 (Computed Observable)。当其底层的可观察对象发生变化时,它会自动保持同步。
MobX 中的状态 = 核心状态 + 派生状态
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';
}
在上面的例子中,如果 `firstName` 和/或 `lastName` 发生变化,`fullName` 会自动保持同步。
操作
动作是您改变可观察对象的方式。动作并非直接修改它们,而是
为修改操作增加了语义。例如,执行一个 `increment()` 动作比简单地执行 `value++`
更有意义。此外,动作还会将
所有的通知批量处理,并确保只有在动作完成后才会通知变更。
因此,观察者只会在动作原子性地完成后才会收到通知。
请注意,动作也可以嵌套,在这种情况下,通知会在
最顶层的动作完成后发出。
final counter = Observable(0);
final increment = Action((){
counter.value++;
});
在类中创建动作时,您可以利用注解的优势!
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++;
}
}
异步动作
MobX.dart 自动处理异步动作,不需要用 `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
}
反应
反应完成了 MobX 的**可观察对象**、**动作**和**反应**三要素。它们是
响应式系统的观察者,每当它们所跟踪的可观察对象
发生变化时,它们就会收到通知。反应有以下几种类型。所有这些
都会返回一个 `ReactionDisposer`,这是一个可以被调用以销毁反应的函数。
反应的一个*显著特点*是它们*自动跟踪*所有可观察对象,无需任何显式连接。在反应中*读取一个可观察对象*的行为就足以跟踪它!
您用 MobX 编写的代码几乎没有任何冗余的仪式代码!
ReactionDisposer autorun(Function(Reaction) fn)
立即运行反应,并在 `fn` 内部使用的任何可观察对象发生变化时再次运行。
fn.
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();
// Prints:
// Hello World
// Hello MobX
ReactionDisposer reaction<T>(T Function(Reaction) predicate, void Function(T) effect)
监视 `predicate()` 函数内部使用的可观察对象,并在 `predicate` 返回不同值时运行 `effect()`。
只有 `predicate()` 内部的可观察对象会被跟踪。
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();
// Prints:
// Hello MobX
ReactionDisposer when(bool Function(Reaction) predicate, void Function() effect)
监视 `predicate()` 内部使用的可观察对象,并在其返回 `true` *时*运行 `effect()`。`effect()` 运行后,`when` 会自动销毁。所以您可以将 *when* 看作是*一次性*的 `reaction`。您也可以提前销毁 `when()`。
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
Future<void> asyncWhen(bool Function(Reaction) predicate)
与 `when` 类似,但返回一个 `Future`,该 `Future` 在 `predicate()` 返回 *true* 时完成。这是一种等待 `predicate()` 变为 `true` 的便捷方式。
final completed = Observable(false);
void waitForCompletion() async {
await asyncWhen(() => _completed.value == true);
print('Completed');
}
Observer
应用中最直观的反应之一是 UI。Observer 小组件(属于 `flutter_mobx` 包的一部分)为其 `builder` 函数中使用的可观察对象提供了一个精细的观察者。每当这些可观察对象发生变化,`Observer` 就会重新构建和渲染。
下面是完整的*计数器*示例。
import 'package:flutter/material.dart';
import 'package:flutter_mobx/flutter_mobx.dart';
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++;
}
}
class CounterExample extends StatefulWidget {
const CounterExample({Key key}) : super(key: key);
@override
_CounterExampleState createState() => _CounterExampleState();
}
class _CounterExampleState extends State<CounterExample> {
final _counter = Counter();
@override
Widget build(BuildContext context) => Scaffold(
appBar: AppBar(
title: const Text('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: 20),
)),
],
),
),
floatingActionButton: FloatingActionButton(
onPressed: _counter.increment,
tooltip: 'Increment',
child: const Icon(Icons.add),
),
);
}