Mobx是如何工作的

mobx工作原理

推荐版本: "mobx-react": "4.1.0", "mobx": "^2.7.0 || ^3.0.0"

1 Mobx 要点

1.1 定义状态并使其可观察

可以任何数据结构来存储状态,如对象、数组、类,打上mobx的标记会变为可观察。

import { observable } from 'mobx';
var appStore = observable({
    timer: 0;
});

1.2 创建视图响应状态变化

mobx 以最小限度更新视图,任何函数都可以成为响应式视图观察自身数据。 

import {observer} from 'mobx-react';
@observer
class TimerView extends React.Component {
    render() {
        return (
            <button onClick={this.onReset.bind(this)}>
                Seconds passed: {this.props.appState.timer}
            </button>
        );
    }
    onReset() {
        this.props.appState.resetTimer();
    }
};
ReactDOM.render(<TimerView appState={appStore} />, document.body);

1.3 更改状态

mobx会用简单直观的方式更改状态,使用action(可以配置Mobx强制使用action更新)或者直接修改

2 概念及原则

2.1 State 状态

状态是驱动应用的数据。像待办事务列表的特定状态,还有像当前已选元素的视图状态。 
状态就像有数据的 Excel表格。

2.2 derivations 衍生

什么是衍生, 源自状态并且不会再有进一步的相互作用的东西就是衍生。 

  • 用户界面 
  • 衍生数据,剩下的待办事项 
  • 后端集成,比如吧变化发送到服务器端 
    mobx 集成了两种类型的衍生

Computed values计算属性值

使用纯函数从当前可观察状态中衍生出的值。 

Reactions 反应

是当 State 改变时需要自动发生的副作用。。需要有一个桥梁链接 函数式编程 和 响应式编程 
初次使用mobx会经常使用reactions,但是推荐使用computed,回到表格的概念,公式是计算值的衍生。 

Actions 动作

动作 任意一段可以改变状态的代码。 用户操作,后端数据推送,预定事件等。 
动作 类似在表格单元格中输入一个新值。 mobx中可以显示的定义动作,@action . 

3 原则

mobx支持单向的数据流,动作来改变状态,从而 状态State 的改变会更新受影响的视图。 
action ---> state ---> view 
当 State 改变时,所有衍生都会进行原子级自动更新 因此不可能观察到中间值 
全部的 衍生 默认都是同步更新 因此在动作之后,可以安全的检查计算值 
计算值 是延迟更新的。 任何不在使用状态的计算值都不会更新,直到需要时会进行 副作用(IO),不使用时会自动垃圾回收 
计算值 不应该去改变状态,应该是一个纯洁的副作用。 

4 核心API

主要api: Computed 、 observable 、 reactions 、 actions  

4.1 observable

observable(value);
@observable property = value;

Observable 观察的值可以是基本类型、引用类型、普通对象、类实例、数组和映射。 
主要类型转换规则,或者通过装饰器微调(修饰class,函数)

  • 如果被观察 value 是ES6实例,会返回一个新的Observe Map,基于ES6。如果不只是在更改某个entry时修改 
    而是,在添加或删除其他entry时做出反应,Observe Map 会很有用。 
  • 如果被观察 value 是数组,会返回一个 Observe Array 
  • 如果 value 是么有原型的对象(对象可以灭有原型)或者原型是 Object.prototype ,对象会被克隆并且所有属性会被转换成可观察的 Observe Object 
  • 如果 value 是有原型的,例如函数,数组,可以有4中方法处理 Boxed Observer
    • 显示的调用 observable.box(value) 有点神奇
    • @observable 常用
    • 调用 decorate()
    • 类中引入 extendObservable() 来引入属性 可用 装饰器默认是有感染的,observalbe 被自动应用于数据结构包含的任何值,
      observable 是 extendObservable(this, {prototype: value}) 的语法糖 observable.object(obj, decorator, option) 默认这些值都会转换成可观察 
      observable.array(obj, option) 会生成一个observable 数组,如果不想每个值都被观测,可设置 {deep: false} 
      observable.map(obj, option) 无需局限于字符串

4.2 装饰器 Decorator

可用装饰的列表是这些:

  • observable.deep 默认的 observable 装饰器 
  • computed 创建一个衍生,就是能自动获取已修改值的函数并返回新值 
  • action 创建 动作 
  • action.bound 创建有范围的动作 
class TodoList {
    todos = {}
    get unfinishedTodoCount() {
        return values(this.todos).filter(todo => !todo.finished).length
    }
    addTodo() {
        const t = new Todo()
        t.title = 'Test_' + Math.random()
        set(this.todos, t.id, t)
    }
}
decorate(TodoList, {
    todos: observable,
    unfinishedTodoCount: computed,
    addTodo: action.bound
})   // 对类 Observable 转换 

4.3 计算属性 Computed

用法好几种,看起来只有一些细微的差别:

  • computed( () => expression) 
  • computed( () => expression, (newvalue) => void ) 
  • computed( () => expression, option ) 
    @computed({ equals: compareFn }) get property() { return expression; } 
    @computed get classProperty() { return expression; } 

Computed 自带很多操作属性 控制 Computed 行为 

  • 比较器算法 equals: (value, value) => boolean 用来重载默认检测规则的比较函数。 内置比较器有: comparer.identity, comparer.default, comparer.structural 
  • 追踪 其他observable 类型属性值,等待返回之后在做计算 requiresReaction: boolean 在重新计算衍生属性之前,等待追踪的 observables 值发生变化

- get: () => value 
- set: (value) => void 
- keepAlive: boolean 保持计算值活动,不光是在值发生变化之后。 

4.4 动作 Actions

任何用来 修改状态 的东西 
建议在任何更改 observable 或者有副作用的函数上进行 Actions修饰 

4.5 流处理 Flow

flow(function* (args) {}) 
flow() 接收 generator 函数作为他的唯一输入 
flow 的关键作用是 处理异步代码时确保代码被action包装 ,因为正常的 observable state 对异步操作无法通过 enforceActions 检查。 
神奇的flow可以解决这个异步不跟踪的问题 
注意,异步函数必须是 generator ,而且在内部只能 yield promises 

import { configure, flow } from 'mobx';

// 不允许在动作外部修改状态 严格模式的意思
configure({ enforceActions: true });

class Store {
    @observable githubProjects = [];
    @observable state = "pending"; // "pending" / "done" / "error"


    fetchProjects = flow(function* fetchProjects() { // <- 注意*号,这是生成器函数!
        this.githubProjects = [];
        this.state = "pending";
        try {
            const projects = yield someAsyncProcess(); // 用 yield 代替 await
            const filteredProjects = somePreprocessing(projects);

            // 异步代码自动会被 `action` 包装
            this.state = "done";
            this.githubProjects = filteredProjects;
        } catch (error) {
            this.state = "error";
        }
    })
}

Flows 可以撤销,调用promise的cancel() 方法会停止异步状态取值, 会继续执行 finally 子句 。

5 observables 做出响应

5.1 computed

计算值是可以根据现有的状态或其他计算值衍生的值。 
概念上来讲,他们和表格中的值十分相似,比如汇总80分以上的同学。 
计算属性 可以使实际可修改的值尽可能的小,计算属性也是高度优化过的,可以多用 

5.2 computed & autorun

声明式的创建计算属性,可以在类任意的属性上使用装饰器

import { observable, computed } from 'mobx';

class orderline {
    @observable price = 10;
    @observable amount = 1;

    constructor(price) {
        this.price  = price;
    }
    @computed get total() {
        return this.price * this.amount;
    }

    
}
import { observable, autorun } from 'mobx';

const value = observable(0);
const number = observable(100);

autorun(() => {
  console.log(value.get());
});

value.set(1);
value.set(2);
number.set(101); // 0 1  2  不打印 101 yinwei number 未在autorun内部执行 number.get()/
Mobx 学习 基本写法 * 此处声明式的监控变量,与 ES6 的类修饰不同。 

import { observable, action, computed, toJS } from 'mobx' import { observer } from 'mobx-react'

export default class InstanceStore { @observable value = 1

@action
modifyValue(v) {
    this.value = v
}
@computed get getValue() {
    return this.value * 10;
}

}

computed 直接获取一个计算后的值。

如果一个值需要根据某个state计算,并且也需要被观察则可以使用 @computed autorun 类似

autorun 用于执行一些和值变化有关的操作,比如异步请求,数据处理等

computed 用于根据已有的值,计算出新的值返回一个对观察值追踪的结果 var ins = new InstanceStore(); console.log('value form mobx computed', toJS(ins.getValue()))

autorun 在不需要继续使用的情况可以进行垃圾回收

var numbers = observable([1,2,3]);

var sum = computed(() => numbers.reduce((a, b) => a + b, 0));

var disposer = autorun(() => console.log(sum.get())); // '6'

numbers.push(4);                                                  // '10'

disposer();

numbers.push(5); // 什么也不打印,因为disposer执行是不再对autorun reaction

过期状态值方式如下

var ins = new InstanceStore();

console.log(toJS(ins.value),'get value from mobx');

dispatch 修改值 var ins = new InstanceStore(); ins.modifyValue(1000);

在组件内可以使用观察者模式 @observer class routeCreate extends Component { constructor(props) { super(props); this.store = new InstanceStore(); } ... }

使用 observer 修饰组件,并且在render内部有 mobx 值的引用,组件会多一个生命周期 componentWillReact // redux 改变值的方式是通过拷贝原来的对象生成新的对象,触发组件的componentWillReceiveProps // mobx 是以原始值的基础上生成新的对象,之前的引用不变所以mobx 不会触发ReceiveProps周期


异步处理
mobx 状态值为同步更新。

export default class InstanceStore { @observable value = 1

@action
modifyValue(v) {
    this.value = v;
    setTimeout(this.valueAdd, 100);
}
@action.bound
valueAdd(v) {
    this.value = v + 20;
}

}

// .bound 是js执行环境语法糖
// 过多action ? 需要简化写法
// mobx 自身提供了一个工具函数帮助跟新对应action中的值 runInAction

export default class InstanceStore {

   @observable value = 1

@action
asyncModifyValue(v) {
    this.value = v;
    setTimeout(action('valueAdd', () => {
        this.value = v + 20
    }), 100);
}

@action
asyncModify(v) {
    this.value = v;
    setTimeout(runInAction(() => {
        this.value = v + 20
    }), 100);
}

}

//  异步action,action可以这样写
@asyncAction
changeValue() {
    this.value = 0;
    const data = yield Promise.resolve(1)
    this.value = data;
}


toJS 将mobx state 序列转换为js可识别类型?

更新action的约束
mobx 非强制使用action改变state;如果要加强制action触发state可以通过 
Mobx.configure({enforceActions: true}) 加限制条件,强制通过action更新,适用于大型项目



以下是遗留问题
* 1, mobx 是否是同步更新 是
* 2, mobx toJS是如何实现的  
* 3,store对应单个变量,会按照类型预留数组空间,是什么原因
* 4,使用toJS获取数据,需要在class名称上面加 @observer 吗
* 5,为什么mobx取值,是如此的简介?,而且是支持多状态
* 6,extendObservable 可以按照扩展的方式 装饰函数或class里的对象
posted @ 2019-07-21 14:14  kimoon  阅读(921)  评论(0编辑  收藏  举报