Skip to content
On this page

mobx

https://cn.mobx.js.org/

mobx是一个简单可扩展的状态管理库,相比较于redux,它:

  • 开发难度低

  • 开发代码量少

  • 渲染性能好

浏览器兼容性

MobX >=5 版本运行在任何支持 ES6 proxy 的浏览器。 MobX 4 可以运行在任何支持 ES5 的浏览器上,而且也将进行持续地维护。MobX 4 和 5 的 API 是相同的,并且语义上也能达到相同的效果。 MobX 6 「最新版本」移除了装饰器的操作(因为装饰器不是JS标准规范)!

想要使用mobx,首先需要让项目支持JS装饰器语法!

bash
yarn add @babel/plugin-proposal-decorators @babel/plugin-proposal-class-properties
1

package.json

json
"babel": {
  "presets": [ "react-app" ],
  "plugins": [
    [
      "@babel/plugin-proposal-decorators",
      {
        /* legacy /ˈleɡəsi/:使用历史遗留(Stage-1)的装饰器中的语法和行为。它为提案从 Stage-1 到当前阶段 Stage-2 平稳过渡作铺垫*/
        "legacy": true
      }
    ],
    [
       "@babel/plugin-proposal-class-properties",
       {
         /* loose=false时,是使用Object.defineProperty定义属性,loose=ture,则使用赋值法直接定义 */
         "loose": true
       }
     ]
  ]
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19

mobx 第五代版本的运用

bash
yarn add mobx@5 mobx-react@6
1

初窥mobx

实现一个简单的计数器累加效果

JavaScript
import { observable, action } from 'mobx';
import { observer } from 'mobx-react';

// 公共状态管理
class Store {
    @observable num = 10;
    @action handle() {
        this.num++;
    }
}
const store = new Store;

//组件监听
@observer
class Demo extends React.Component {
    render() {
        return <div>
            <span>{store.num}</span>
            <br />
            <button onClick={() => {
                store.handle();
            }}>按钮</button>
        </div>;
    }
}

// 函数组件不支持装饰器,我们则基于observer把其执行即可
/* const Demo = observer(function Demo() {
    return <div>
        ...
    </div>;
}); */

const root = ReactDOM.createRoot(document.getElementById('root'));
root.render(
    <Demo />
);
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37

observable

一个实现“监听值变化”的函数或者装饰器! 先来看一个效果

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

class Store {
    // 只有基于 observable 装饰器修饰的属性,在可以在修改其值后,监测到它的变化
    @observable x = 10;
}
let store = new Store;

// 监听用到的依赖,当依赖改变时会执行callback「最开始立即执行一次」
autorun(() => {
    console.log('autorun:', store.x);
});

// 一秒钟后改变内容
setTimeout(() => {
    store.x = 100;
}, 1000);
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17

探索 observable 的原理

JavaScript
import { observable, observe } from 'mobx';
let obj = observable({
    x: 10,
    y: 20
});
// console.log(obj); //对象是经过ES6 Proxy做了劫持处理的
// observe:当监听的对象做出变化时,触发callback
observe(obj, change => {
    console.log('内容改变了:', change);
});
obj.x = 100;

//----
// observable不能直接对原始值类型进行监听,需要基于.box处理
let x = observable.box(10);
observe(x, change => {
    console.log('内容改变了:', change);
});
console.log(x, x.get());
x.set(1000);
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20

所以我们以后创建的公共状态信息,前面都要设置 @observable 装饰器!!

computed计算属性

JavaScript
import { observable, autorun, computed, reaction } from 'mobx';
class Store {
    @observable x = 10;
    @observable count = 3;
    @observable price = 120;
    // 设置具备计算缓存的计算属性:依赖的状态值没有变化,方法不会重新执行,使用之前计算缓存的结果
    @computed get total() {
        console.log('OK');
        return this.count * this.price;
    }
}
let store = new Store;

autorun(() => {
    console.log('autorun:', store.total, store.x);
});
// 相比较于autorun,提供更精细的管控「第一次不会触发」
reaction(
    () => [store.total, store.x],
    () => {
        console.log('reaction:', store.total, store.x);
    }
);

setTimeout(() => {
    store.x = 1000;
    store.count = 10;
}, 1000);
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28

action修改公共状态的方法

JavaScript
import { observable, autorun, action, configure } from 'mobx';

// 设定只能基于action方法修改状态值
configure({
    enforceActions: "observed"
});

class Store {
    @observable x = 10;
    @action changeX(val) {
        this.x = val;
    }
}
let store = new Store;

autorun(() => {
    console.log('autorun:', store.x);
});
setTimeout(() => {
    store.changeX(1000);
    // store.x = 2000; //Uncaught Error: [mobx] Since strict-mode is enabled, changing observed observable values outside actions is not allowed.
}, 1000);
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
JavaScript
class Store {
    @observable x = 10;
    // .bound确保this永远是实例
    @action.bound changeX(val) {
        this.x = val;
    }
}
let store = new Store;
...
setTimeout(() => {
    let changeX = store.changeX;
    changeX(1000);
}, 1000);
1
2
3
4
5
6
7
8
9
10
11
12
13
JavaScript
class Store {
    @observable x = 10;
}
let store = new Store;

autorun(() => {
    console.log('autorun:', store.x);
});
setTimeout(() => {
    // 基于runInAction代替action修饰器「即便设置enforceActions配置项,它也是被允许的」,和action修饰器具备相同的效果!!
    runInAction(() => {
        store.x = 1000;
    });
}, 1000);
1
2
3
4
5
6
7
8
9
10
11
12
13
14

实现异步派发

JavaScript
// 模拟从服务器获取数据
const query = () => {
    return new Promise(resolve => {
        setTimeout(() => {
            resolve(1000);
        }, 1000);
    });
};

class Store {
    @observable x = 10
    @action.bound async changeX() {
        let res = 0;
        try {
            res = await query();
        } catch (_) { }
        this.x = res;
    }
}
let store = new Store;
autorun(() => {
    console.log('autorun:', store.x);
});
store.changeX();
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
沪ICP备20006251号-1