MobX是一个简单有效的状态管理库,以派生(derive)的概念为核心,以观察者模式为手段,达到了修改数据自动更新界面等目的

  • 正因为其本身提供了包装react的方法,可以简洁的改善react组件,所以官网文档和几乎所有教程都以react和ES7的装饰修饰符等特性为切入点

  • 但MobX在传统的ES5环境中也能良好工作,本文尝试以此为出发点,探讨在既有的非react项目中直接引入MobX并用其整理重构老代码的方法

  • 没有babel、webpack、JSX...那么多的套路!直接上手先,走你~

    [II]. 可观察的类型

    语法 mobx.observable(value)

    2.1 普通对象

  • 普通对象指不是通过构造函数创建的,没有特定原型对象的 plain object

  • 如果一个普通对象被传递到 observable() 中,其所有属性都会成为可观察的,并被拷贝到一个副本中(对副本的更改也同时影响原始对象的值)

  • 默认是递归处理的,如果一个属性是对象或数组,其元素也会被观察

  • var $ctn = document.querySelector('#container');
    var obj = {
        a: 1,
            c: 3,
    var observ = mobx.observable(obj);
    setTimeout(function(){
        observ.b.c = 666;
    }, 1000);
    mobx.autorun(function(){
        //元素内容变为 [new value] c: 666
        $ctn.innerHTML = "[new value] c: " + observ.b.c;
    setTimeout(function(){
        alert([obj.b.c, observ.b.c]; //666, 666
    }, 2000);

    observable.shallowObject(value) 方法可以实现“浅观察”,只自动响应“浅层”的子属性

    var $ctn1 = document.querySelector('#container1');
    var $ctn2 = document.querySelector('#container2');
    var $info = document.querySelector('#info');
    var obj = {
        a: 1,
            c: 3,
    var observ1 = mobx.observable.shallowObject(obj);
    var observ2 = mobx.observable.shallowObject(obj);
    mobx.autorun(function(){
        //元素内容变为 [new value of observ1] c: 3
        $ctn1.innerHTML = "[new value of observ1] c: " + observ1.b.c;
    mobx.autorun(function(){
        //元素内容变为 [new value of observ2] c: 999, a: 555
        $ctn2.innerHTML = "[new value of observ2] c: " + observ2.b.c + ", a: " + observ2.a;
    setTimeout(function(){
        observ1.b.c = 666;
        observ2.b.c = 999;
        observ2.a = 555;
    }, 1000);
    setTimeout(function(){
        $info.innerHTML = [obj.b.c, observ1.b.c, observ2.b.c].join(','); //999, 999, 999
    }, 2000);

    2.2 数组

  • 和对象类似的是,向 observable() 传递一个数组参数,数组中的每一项也会变为可观察的,且默认为递归处理的深度观察

  • 和对象类似,数组也有一个浅观察的方法 observable.shallowArray(value)

  • Array.isArray(observable([])) 会返回fasle,但可用 Array.isArray(observable([]).slice()) 达到正确的效果

  • 与原生数组对象的 sort() reverse() 方法不同的是,可观察数组的这两个方法返回相应结果的一个数组副本,而不影响原数组

  • 除了内建的数组方法,可观察数组也扩展了如下方法:

  • clear()

  • replace(newItems)

  • find(predicate: (item, index, array) => boolean, thisArg?, fromIndex?)

  • remove(value)

  • peek() : 和 slice() 类似,返回一个安全的原生数组

  • intercept(change=> change|null ) : 拦截更改,并可指定使用自定义后的更改

  • observe(change=>{}, fireImmediately? = false) : 监听更改

  • 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(", ")
    // Prints: 'Remaining: Make coffee'
    todos[0].completed = false;
    // Prints: 'Remaining: Spoil tea, Make coffee'
    todos[2] = { title: 'Take a nap', completed: false };
    // Prints: 'Remaining: Spoil tea, Make coffee, Take a nap'
    todos.shift();
    // Prints: 'Remaining: Make coffee, Take a nap'

    2.3 Map

  • observable.map(values?) 可以创建一个可观察的Map类型

  • 可选的一个参数,可以是一个对象、一个ES6 Map,或是一个键值字符串数组

  • 类似于对象,可以用 observable.shallowMap(values) 实现浅观察

  • var a = mobx.observable.map({a:111})
    console.log('map1 ', a.get('a')); //111
    var es6Map = new Map();
    es6Map.set('a', 222);
    var b = mobx.observable.map(es6Map);
    console.log('map2', b.get('a')); //222
    var c = mobx.observable.map(['a', 'b', 'c']);
    console.log(c.toJS()); //{a: undefined, b: undefined, c: undefined}

    和ES6规范中相同的方法包括:

  • has(key)

  • set(key, value)

  • delete(key)

  • get(key)

  • keys()

  • values()

  • entries()

  • forEach(callback:(value, key, map) => void, thisArg?)

  • clear()

  • 不同于ES6规范的方法包括:

  • toJS() - 得到一个浅复制的javascript对象( 深复制用mobx.toJS(map) )

  • merge(values) - 合并新的对象到Map中,参数格式同初始化方法

  • replace(values) - 替换,相当于 map.clear().merge(values)

  • intercept(interceptor)

  • observe(listener, fireImmediately?)

  • 2.4 基本类型值和引用

  • 所有JS的基本值都是不可变的,因此单个变量无法被观察

  • MobX将这些类型转换成可观察的“boxed value”

  • 转换后的对象可调用如下方法:

  • get() - 取得当前值

  • set(value) - 替换当前值,并通知所有观察者方法

  • intercept(interceptor)

  • observe(callback: (change) => void, fireImmediately = false)

  • const cityName = observable("Vienna");
    console.log(cityName.get());
    // prints 'Vienna'
    cityName.observe(function(change) {
        console.log(change.oldValue, "->", change.newValue);
    cityName.set("Amsterdam");
    // prints 'Vienna -> Amsterdam'
  • observable.shallowBox(value) 基于 observable.ref() 实现了浅观察

  • 这意味着只观察引用本身,而其值并不会被自动观察

  • var str2 = "world";
    var pobj2 = mobx.observable(str2);
    var str3 = "!!!";
    var pobj3 = mobx.observable.shallowBox(str3);
    mobx.autorun(function(){
        console.log(str2, pobj2.get());
        console.log(str3, pobj3.get());
    setTimeout(function() {
        console.log('[after 1s]');
        mobx.runInAction(()=>{
            pobj2.set("wo____rld");
            pobj3.set("~~~");
    }, 1000);
    setTimeout(function() {
        console.log('[after 2s]');
        mobx.runInAction(()=>{
            pobj2.set({a: 11});
            pobj3.set({b: 22});
    }, 2000);
    /* autorun输出:
    world world
    !!! !!!
    [after 1s]
    world wo____rld
    !!! ~~~
    [after 2s]
    world {$mobx: ObservableObjectAdministration}
    !!! {b: 22}
    

    2.5 类实例

    对于类实例,需要在构造函数中或对实例对象调用mobx.extendObservable(targetName, ...props)方法:

    var Person = function(firstName, lastName) {
        //不需要观察的实例属性
        this.id = (new Date).getTime();
        //需要观察的实例属性
        mobx.extendObservable(this, {
            firstName: firstName,
            lastName: lastName
    var matthew = new Person("Matthew", "Henry");
    //对已初始化的实例增添新的可观察属性
    mobx.extendObservable(matthew, {
        age: 25
    

    类实例中的描述符

  • 描述符被用来对指定的属性定义特殊的行为

  • 比如用observable.ref()来浅观察引用、用computed()来声明一个派生属性,或用action()定义一个改变状态的动作

  • var Person2 = function(firstName, lastName) {
        this.id = (new Date).getTime();
        mobx.extendObservable(this, {
            firstName: mobx.observable.ref(firstName),
            lastName: mobx.observable.ref(lastName),
            fullName: mobx.computed(function() {
                return this.firstName + " " + this.lastName
            setLastName: mobx.action(function(name) {
                this.lastName = name;  
    var p2 = new Person2('tom', 'jerry');
    p2.setLastName('trump');
    p2.firstName = 'donald';
    console.log(p2.fullName); //用computed定义的派生属性用法上类似getter

    类实例中的 getter/setter

  • 也可以用getter定义一个派生属性

  • 配对的setter是可选的,用来定义一个action; 且该方法不能直接操作派生属性,而是通过改变核心状态影响它

  • var Person3 = function(firstName, lastName) {
        this.id = (new Date).getTime();
        mobx.extendObservable(this, {
            firstName: firstName,
            lastName: lastName,
            get fullName() {
                return this.firstName + " " + this.lastName
            set fullName(newValue) {
                var parts = newValue.split(" ")
                this.firstName = parts[0]
                this.lastName = parts[1]
    var p3 = new Person3;
    p3.fullName = "ivanka trump";
    console.log(p3.fullName, p3.firstName);

    类实例中的浅观察

  • extendShallowObservable(value)同样基于observable.ref()实现了浅观察

  • observable.deep(prop)被用来对某个属性单独指定深观察

  • * 原创文章转载请注明出处

    -------------------------------------

    长按二维码关注我们的公众号哦: