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) 

// 工具函数,它接收代码块并在(异步的)动作中执行。这对于即时创建和执行动作非常有用

 

 posted on 2019-10-11 14:38  不了无明  阅读(435)  评论(0编辑  收藏  举报