Mobx4 Observable

observable

1
2
3
// 用法
observable(value)
@observable classProperty = value

Observable 值可以是 JS 基本数据类型、引用类型、普通对象、类实例、数组和映射。

1
2
3
4
5
6
7
8
9
10
11
const map = observable.map({ key: "value" });
map.set("key", "new value");
const list = observable([1, 2, 4]);
list[2] = 3;
const person = observable({
firstName: "Clive Staples",
lastName: "Lewis",
});
person.firstName = "C.S.";
const temperature = observable.box(20);
temperature.set(25);

@observable

装饰器可以在 ES7 或者 TypeScript 类属性中属性使用,将其转换成可观察的。

1
2
3
4
5
6
7
8
import { observable, computed } from "mobx";
class OrderLine {
@observable price = 0;
@observable amount = 1;
@computed get total() {
return this.price * this.amount;
}
}

Observable 对象

1
observable.object(props, decorators?, options?)

如果把一个普通的 JavaScript 对象传递给 observable 方法,对象的所有属性都将被拷贝至一个克隆对象并将克隆对象转变成可观察的。

  • 当通过 observable 传递对象时,只有在把对象转变 observable 时存在的属性才会是可观察的。稍后添加到对象的属性不会变为可观察的,除非使用 set 或 extendObservable。
  • 只有普通的对象可以转变成 observable 。对于非普通对象,构造函数负责初始化 observable 属性。要么使用 @observable 注解,要么使用 extendObservable 函数。
  • 属性的 getter 会自动转变成衍生属性,就像 @computed 所做的。
  • observable 是自动递归到整个对象的。在实例化过程中和将来分配给 observable 属性的任何新值的时候。Observable 不会递归到非普通对象中。
  • 传入 { deep: false } 作为第三个参数可以禁用属性值的自动转换
  • 传入 { name: “my object” } 为本对象赋予友好的调试名称

Observable 数组

和对象类似,可以使用 observable.array(values?) 或者将数组传给 observable,可以将数组转变为可观察的。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
import { observable, autorun } from "mobx";
var todos = observable([
{ title: "Spoil tea", completed: true },
{ title: "Make coffee", completed: false },
]);
autorun(() => {
console.log(
"Remaining:",
todos
.filter((todo) => !todo.completed)
.map((todo) => todo.title)
.join(", ")
);
});
// 输出: 'Remaining: Make coffee'
todos[0].completed = false;
// 输出: 'Remaining: Spoil tea, Make coffee'
todos[2] = { title: "Take a nap", completed: false };
// 输出: 'Remaining: Spoil tea, Make coffee, Take a nap'
todos.shift();
// 输出: 'Remaining: Make coffee, Take a nap'

请记住无论如何 Array.isArray(observable([])) 都将返回 false ,所以无论何时当你需要传递 observable 数组到外部库时,通过使用 array.slice() 在 observable 数组传递给外部库或者内置方法前创建一份浅拷贝。

  • intercept(interceptor) - 可以用来在任何变化作用于数组前将其拦截。
  • observe(listener, fireImmediately? = false)监听数组的变化。回调函数将接收表示数组拼接或数组更改的参数,它符合 ES7 提议。它返回一个清理函数以用来停止监听器。
  • clear() - 从数组中删除所有项。
  • replace(newItems) - 用新项替换数组中所有已存在的项。
  • find(predicate: (item, index, array) => boolean, thisArg?) - 基本上等同于 ES7 的 Array.find 提议。
  • findIndex(predicate: (item, index, array) => boolean, thisArg?) - 基本上等同于 ES7 的 Array.findIndex 提议。
  • remove(value) - 通过值从数组中移除一个单个的项。如果项被找到并移除的话,返回 true 。
  • peek() - 和 slice() 类似, 返回一个有所有值的数组并且数组可以放心的传递给其它库。

Observable 映射

1
observable.map(values, options?)

创建一个动态键的 observable 映射。如果你不但想对一个特定项的更改做出反应,而且对添加或删除该项也做出反应的话,那么 observable 映射会非常有用。

下列 observable 映射所暴露的方法是依据 ES6 Map 规范:

  • has(key) - 返回映射是否有提供键对应的项。注意键的存在本身就是可观察的。
  • set(key, value) - 把给定键的值设置为 value 。提供的键如果在映射中不存在的话,那么它会被添加到映射之中。
  • delete(key) - 把给定键和它的值从映射中删除。
  • get(key) - 返回给定键的值(或 undefined)。
  • keys() - 返回映射中存在的所有键。插入顺序会被保留。
  • values() - 返回映射中存在的所有值。插入顺序会被保留。
  • entries() - 返回一个(保留插入顺序)的数组,映射中的每个键值对都会对应数组中的一项 [key, value]。
  • forEach(callback:(value, key, map) => void, thisArg?) - 为映射中每个键值对调用给定的回调函数。
  • clear() - 移除映射中的所有项。
  • size - 返回映射中项的数量。

以下函数不属于 ES6 规范,而是由 MobX 提供:

  • toJS() - 将 observable 映射转换成普通映射
  • toJSON(). 返回此映射的浅式普通对象表示。(想要深拷贝,请使用 mobx.toJS(map))。
  • intercept(interceptor) - 可以用来在任何变化作用于映射前将其拦截。参见 observe & intercept。
  • observe(listener, fireImmediately?) - 注册侦听器,在映射中的每个更改时触发,类似于为 Object.observe 发出的事件。想了解更多详情,请参见 observe & intercept。
  • merge(values) - 把提供对象的所有项拷贝到映射中。values 可以是普通对象、entries 数组或者 ES6 字符串键的映射。
  • replace(values) - 用提供值替换映射全部内容。是 .clear().merge(values) 的简写形式。

Boxed Values

在极少数情况下,拥有一个不属于某个对象的可观察的“原始类型值”还是很方便的。对于这种情况,可以创建一个 observable box 以便管理这样的原始类型值。

1
observable.box(value);

observable.box(value) 接收任何值并把值存储到箱子中。使用 .get() 可以获取当前值,使用 .set(newValue) 可以更新值。

1
2
3
4
5
6
7
8
9
import { observable } from "mobx";
const cityName = observable.box("Vienna");
console.log(cityName.get());
// 输出 'Vienna'
cityName.observe(function (change) {
console.log(change.oldValue, "->", change.newValue);
});
cityName.set("Amsterdam");
// 输出 'Vienna -> Amsterdam'