observable
observable
observable(value) @observable classProperty = value
// 如果 value 是ES6 Map的实例: 会返回一个新的 Observable Map。不只关注某个特定entry的更改,而且对更改添加或删除其他entry时也做出反应。 // 如果 value 是数组,会返回一个 Observable Array。 // 如果 value 是没有原型的对象或它的原型是 Object.prototype,那么对象会被克隆并且所有的属性都会被转换成可观察的。 // 如果 value 是有原型的对象,JavaSript 原始数据类型或者函数,值不会发生变化。如果你需要 Boxed Observable: 1, 显式地调用 observable.box(value)
// observable.box(value, options?), // 使用 get() 方法可以得到盒子中的当前value,而使用 set() 方法可以更新value // 使用 {deep: false} 选项会禁用新值被observable。
2, 在类定义时使用 @observable , // 是extendObservable(this, { property: value })
的语法糖
3, 调用 decorate()
4, 在类中使用 extendObservable() 来引入属性
// 要想使用 @observable 装饰器,首先要确保编译器(babel 或者 typescript)中 装饰器是启用的。 // 默认情况下将一个数据结构转换成可观察的是有感染性的,这意味着 observable 被自动应用于数据结构包含的任何值,或者将来会被该数据结构包含的值。这个行为可以通过使用 装饰器 来更改
// 为提供的对象创建一个克隆并将其所有的属性转换成 observable // 使用 {deep: false} 选项时只有属性会转换成 observable 引用,而值不会改变(这也适用于将来分配的任何值)。 observable.object(value, decorators?, options?) observable.array(value, options?) // 基于提供的值来创建一个新的 observable 数组。 observable.map(value, options?) // 基于提供的值来创建一个新的 observable 映射 创建动态的键集合并且需要能观察到键的添加和移除,可以自由使用任何键而无需局限于字符串。
extendObservable(target, properties, decorators?, options?) // extendObservable 增强了现有的对象,不像 observable.object 是创建一个新对象 // 如果新的属性不应该具备感染性,extendObservable(target, props, decorators?, {deep: false})
装饰器(Decorators)
// 如果没有传入装饰器,默认为对任意键值对使用 observable.deep
,对 getters 使用 computed
。
observable.deep: // 所有 observable 都使用的默认的装饰器。它可以把任何指定的、非原始数据类型的、非 observable 的值转换成 observable。 observable.ref: // 禁用自动的 observable 转换,只是创建一个 observable 引用。 observable.shallow: // 只能与集合组合使用。 将任何分配的集合转换为浅 observable (而不是深 observable)的集合。 换句话说, 集合中的值将不会自动变为 observable。 computed: // 创建一个衍生属性computed.struct
:
// 与computed
相同,但是只有当视图产生的值与之前的值结构上有不同时,才通知它的观察者 action: // 创建一个动作action.bound
:
// 创建一个动作, 并将this
绑定到了实例observable.struct
:
// 就像ref
, 但会忽略结构上等于当前值的新值
// @decorator 语法应用装饰器 import {observable, action} from 'mobx'; class TaskStore { @observable.shallow tasks = [] @action addTask(task) { /* ... */ } }
// decorate() 传入属性装饰器 import {observable, action} from 'mobx'; const taskStore = observable({ tasks: [], addTask(task) { /* ... */ } }, { tasks: observable.shallow, addTask: action })
// 使用 decorate 时,所有字段都应该指定 (毕竟,类里的非 observable 字段可能会更多) class Person { name = "John" age = 42 showAge = false get labelText() { return this.showAge ? `${this.name} (age: ${this.age})` : this.name; } setAge(age) { this.age = age; } } decorate(Person, { name: observable, age: observable, showAge: observable, labelText: computed, setAge: action })
// 想要在单个属性上应用多个装饰器的话,可以传入一个装饰器数组。多个装饰器应用的顺序是从右至左。 import { decorate, observable } from 'mobx' import { serializable, primitive } from 'serializr' import persist from 'mobx-persist' class Todo { id = Math.random(); title = ''; finished = false; } decorate(Todo, { title: [serializable(primitive), persist('object'), observable], finished: [serializable(primitive), observable] })
Computed values(计算值)
// @computed import {observable, computed} from "mobx"; class OrderLine { @observable price = 0; @observable amount = 1; constructor(price) { this.price = price; } @computed get total() { return this.price * this.amount; } }
// 使用 decorate 引入 import {decorate, observable, computed} from "mobx"; class OrderLine { price = 0; amount = 1; constructor(price) { this.price = price; } get total() { return this.price * this.amount; } } decorate(OrderLine, { price: observable, amount: observable, total: computed })
// observable.object 和 extendObservable 都会自动将 getter 属性推导成计算属性 const orderLine = observable.object({ price: 0, amount: 1, get total() { return this.price * this.amount } })
// 计算值的 setter // setters 不能用来直接改变计算属性的值,可以用来作“逆向”衍生。 const orderLine = observable.object({ price: 0, amount: 1, get total() { return this.price * this.amount }, set total(total) { // 从 total 中推导出 price this.price = total / this.amount } })
// 注意: 永远在 getter 之后 定义 setter,一些 TypeScript 版本会知道声明了两个具有相同名称的属性。 class Foo { @observable length = 2; @computed get squared() { return this.length * this.length; } set squared(value) { this.length = Math.sqrt(value); } }
// computed 还可以直接当做函数来调用, 可用于传递在box中计算值 import {observable, computed} from "mobx"; var name = observable.box("John"); var upperCaseName = computed(() => name.get().toUpperCase() // 使用.get()
来获取计算的当前值 );
//.observe(callback)
来观察值的改变 var disposer = upperCaseName.observe(change => console.log(change.newValue)); name.set("Dave"); // 输出: 'DAVE'
computed 接收的第二个选项参数对象选项 name: 字符串, 在 spy 和 MobX 开发者工具中使用的调试名称 context: 在提供的表达式中使用的 this set: 要使用的setter函数。 没有 setter 的话无法为计算值分配新值。 如果传递给 computed 的第二个参数是一个函数,那么就把会这个函数作为 setter equals: 默认值是 comparer.default 。它充当比较前一个值和后一个值的比较函数。 MobX 提供了三个内置 comparer (比较器) comparer.identity: 使用恒等 (===) 运算符来判定两个值是否相同。 comparer.default: 等同于 comparer.identity,但还认为 NaN 等于 NaN comparer.structural: 执行深层结构比较以确定两个值是否相同。 requiresReaction: 对于非常关键的计算值,推荐设置成 true 。避免没有跟踪该值导致计算结果丢失。 keepAlive: 有无观察者均保持计算。这很容易导致内存泄漏,因为它会导致此计算值使用的每个 observable ,并将计算值保存在内存中!
// computed 错误处理 const x = observable.box(3) const y = observable.box(1) const divided = computed(() => { if (y.get() === 0) throw new Error("Division by zero") return x.get() / y.get() }) divided.get() // 返回 3 y.set(0) // OK divided.get() // 报错: Division by zero divided.get() // 报错: Division by zero y.set(2) divided.get() // 已恢复; 返回 1.5
Actions(动作)
// 建议在任何更改 observable 或者有副作用的函数上使用动作 // 动作还能提供非常有用的调试信息 action(fn) action(name, fn) @action classMethod() {} @action(name) classMethod () {} @action boundClassMethod = (args) => { body } @action(name) boundClassMethod = (args) => { body } @action.bound classMethod() {} // 它接收一个函数并返回具有同样签名的函数 // 用 transaction、untracked 和 allowStateChanges 包裹起来 // 默认是使用transaction, 动作会分批处理变化并只在(最外层的)动作完成后通知计算值和反应 // @action 装饰器 不支持使用 setters
// 绑定的动作 // action.bound 可以用来自动地将动作绑定到目标对象。 // 注意,与 action 不同的是,(@)action.bound 不需要一个name参数,名称将始终基于动作绑定的属性。 class Ticker { @observable tick = 0 @action.bound increment() { this.tick++ // 'this' 永远都是正确的 } } const ticker = new Ticker() setInterval(ticker.increment, 1000)
runInAction(name?, thunk) // 工具函数,它接收代码块并在(异步的)动作中执行。这对于即时创建和执行动作非常有用