MobX Dart语言版本.
通过使用透明的函数响应式编程(TFRP)加强你的 Dart 应用中的状态管理。
MobX是一种状态管理库,它让应用程序的响应式数据与 UI 关联起来变得很简单。 这种关联是完全自动的,感觉像是自然发生的一样。作为应用程序开发人员,您仅关注于需要在 UI(或其他任何地方)中使用哪些响应式数据,而不必担心使两者保持同步。
这并不是真正的魔术,但是它确实对正在消费的(可观察的对象)和在哪里(反应)有一些了解,并会自动为您跟踪。当观察对象值改变时,所有反应都将重新运行。有趣的是,从简单的控制台日志,网络调用到重新呈现 UI,这些反应可以是任何东西。
MobX 的 JavaScript 版本非常成熟。 应用程序和 Dart 语言的这种移植旨在带来相同水平的生产力。
我们非常感谢赞助商们,能够让我们成为其开源软件(OSS)计划的一部分。 [Become a sponsor]
更深入地学习 Mob,请看 MobX 快速入门指南. 虽然这本书使用的是 JavaScript 版本的 MobX,但是核心的概念是完全适用于 Dart 和 Flutter 版本的。
MobX 的核心是三个重要的概念:可观察的对象(Observables),动作(Actions)和反应(Reactions)。
可观察对象表示应用程序的响应式状态。它们可以是复杂对象树的简单标量。通过将应用程序的状态定义为可观察树,您可以暴露一个 UI(或应用程序中的其他观察者)使用的_reactive-state-tree_。
一个简单的响应式计数器由以下可观察对象表示:
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 软件包获得。
什么可以被计算,什么应该被计算。保持自动化!
您的应用程序的状态包含 核心状态 和 派生状态 。核心状态是您正在处理的域所固有的状态。例如,如果您有一个 Contact
实体,则 firstName
和 lastName
构成Contact的核心状态。但是,fullName
是派生状态,是通过组合 firstName
和 lastName
获得的。
这种依赖于核心状态或其他派生状态的派生状态称为 可计算观察对象。当其观察的对象更改时,它会自动保持同步。
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
内部使用的可观察值进行任何更改。
import 'package:mobx/mobx.dart';
final 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';
final 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
视为一个一次性的反应。您也可以更早地执行 when()
。
import 'package:mobx/mobx.dart';
final 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
,并且是在 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),
),
);
}
恭喜您已经读到这里🎉🎉🎉。您可以通过几种方式为不断增长的 MobX.dart
社区做出贡献。
- 负责被标注为 "good first issue" 的 issue
- 提出功能、质量提升类型的建议
- 发现并报告 bug
- 修复 bug
- 参与讨论并帮助做决策
- 编写并提升文档,文档是至关重要的!
- 提交 Pull Request
- 参与
感谢棒棒哒小伙伴们! (emoji key):
这个项目遵循 all-contributors 规范。欢迎大家以各种形式进行贡献!