开始
在以前的一篇文章中,半行代码
介绍到在 Flutter 里面使用 MobX
, 今天我们就来聊聊 Flutter 和 MobX 的那些事。
这篇文章的大部分内容可以在 MobX.dart[1] 中看到更原汁原味的介绍。阅读本篇文章阅读约需 30 分钟。
我们先来看看 MobX 是什么,根据README的介绍
代码语言:javascript复制使用透明的函数响应式编程增强 Dart 程序中的状态管理
是前端里大名鼎鼎的 MobX.js
的 Dart 版本。
概念
那么,MobX.Dart 有哪些概念,反应了自己函数响应式编程的特性呢?
这里关系到 MobX
的 3 个重要概念:
•Observables: Observables 表示响应式的状态。状态很容易理解,就是应用程序里面的状态,或者数据。响应式,就是可以感知到,可观察到数据的变化,也就是我们经常接触到的 观察者模式
•Actions: Actions 就是一系列可以引发状态发生变化的动作
•Reactions:上面提到状态是可观察的,那么这里的 Reactions 就是状态的观察者,状态发生改变的时候,他们可以收到数据变化的通知。
关于 Observables,还有一些更多的概念:
Computed Observables
顾名思义,被计算出来的 Observables。在一个应用中,我们实际上有 2 种状态,这里分别称为 core-state
和 derived-state
,core-state
指的就是一个对象原本就存在的状态,例如一个人有自己的firstname和lastname,姓和名是人固有的状态。那么 firstname lastname,就动态的出来了我们”姓名“的结果。这个就是我们的 derived-state
。当然这里是一个很基础的例子,现实中我们的业务可能是几个固有属性,通过一个复杂的计算算出了一个最终的结果。这个最终结果实际上也是这个对象的一种状态,它也需要被感知到变化。
这里套用 MobX
的一张图来表示他的核心概念:
show code
我们来看看 Mobx
的具体用法,套用 Flutter 默认的 计数器点击 1 的例子。
一个简单的计数器可以表示成一个可观察的数字状态,计数器表示为 Counter 对象:
代码语言:javascript复制part 'counter.g.dart';
class Counter = CounterBase with _$Counter;
abstract class CounterBase with Store {
@observable
int value = 0;
@action
void increment() {
value ;
}
}
这里,Mobx
需要借助 builder_runner
这个库生成对应的 _$Counter
类的代码。具体可以自行 Google。
那么示例中计数器的值怎么反应到 UI 呢?这里就要借助我们 Flutter-MobX
里的 Widget 啦
final counter = Counter(); // Instantiate the store
Observer(
builder: (_) => Text('${counter.value}',
style: Theme.of(context).textTheme.display1,
),
),
这里我们使用了一个叫做 Observer
的 Widget, builder方法里面把 counter 的 observable 对象的值作为属性传给 Text。
这里在调用 @action
的函数之后,counter 里面的值变化后会自动体现在 UI 上。
这里我们可以看到,使用了 MobX
,我们可以尽可能的使用 StatelessWidget
,避免了 setState
的混乱。也提升了效率。
那么如何使用 Reaction 完成对他的监听呢,Reaction相关的函数有好几个,这里列举几个比较典型的:
ReactionDisposer autorun(Function(Reaction) fn)
立即执行 fn
代码语言:javascript复制import 'package:mobx/mobx.dart';
String greeting = Observable('Hello World');
final dispose = autorun((_){
print(greeting.value);
});
greeting.value = 'Hello MobX';
dispose();
这里会输出
代码语言:javascript复制Hello World
Hello MobX
ReactionDisposer reaction<T>(T Function(Reaction) predicate, void Function(T) effect)
监听 predicate
里面监听 observable,当 predicate
返回了新的对象的时候,调用 effect
函数。
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
dispose();
输出:
代码语言:javascript复制Hello MobX
ReactionDisposer when(bool Function(Reaction) predicate, void Function() effect)
待条件的响应。在reaction
的基础上加上 predicate
函数返回 true
最佳实践
使用了 MobX
,那么我们的代码该如何组织呢?一般来说,我们的 Store
会按照职责,分到每个业务相关的 Store
去。
那么,一个业务模块,如何组织它和 UI、逻辑呢?官方给出了建议的方式。将 Widegt - Store - Service 结合在一起。
•Widget:UI,状态的可视化表示•Store:处理状态•Service:逻辑操作,包括复杂逻辑,网络请求,本地数据库存储等等
最佳的代码结构如下:
其中:
UI 层应该尽量使用 StatelessWidget
和 Observer
结合, 减少 Widget 的 rebuild 次数,提升性能。
Store里面放的 @observable
对象,因为 Dart 在 Flutter 是不能进行运行时反射的,所以复杂对象需要我们自己进行 observable 的声明。否则不会生效。当需要处理衍生状态的时候,可用 computed
替代。
到这里,其实我们在使用 MobX
的时候可以组织出职责分层很明确的函数响应式应用架构。但是不同的页面如何持有 Store
对象,也成了一个问题,当然这个问题在所有的分层架构里都存在。
最简单的是直接写单例的 store, 但是单例的弊端非常明显。我们需要的是在这几个页面这个对象是同一个,超出这个范围,对象可以销毁,或者使用的是另一个对象。很直接的我们就会需要一个对象管理框架,即 依赖注入
针对这点,官方也给出了自己的建议,可以使用 Provider
这个框架达到依赖注入的目的。在他的官方文档介绍里,也说自己是一个介于 DI 和 状态管理之间的框架。在这篇文章就不赘述 Provider
的使用,感兴趣的朋友可以查看:provider的文档[2]
小结
使用 MobX
,我们可以快速的上手,用一种很简便,容易组织的方式进行 Flutter 的状态管理和代码架构的统一。而且 Store 天然的分开可以写一堆。不会存在 Redux
顶级状态管理难以分而治之的问题。对于异步场景的处理也比 Redux 简单。比较推荐给大家。
References
[1]
MobX.dart: https://mobx.pub/getting-started
[2]
provider的文档: https://pub.dartlang.org/packages/provider