Mobx官方文档

Mobx是一个透明函数响应式编程(Transparently Functional Reactive Programming,TFRP)的状态管理库,它使得状态管理简单可伸缩:

1. 基本概念

  • actions:一些改变状态值(state)的动作。
  • state:可观察的状态值
  • computed value:根据state,用pure function计算出来的值
  • reactions:因state或computed value变化而引起的反应,主要指视图UI重新渲染
  • 2. 基本使用

    npm install mobx --save
    npm install mobx-react --save
    

    创建一个mobx store的实例:

    import {observable, computed, action} from "mobx";
     class Store{
        @observable cities = [];
        @computed
        get total() {
            return this.cities.length;
        @action.bound
        clearCities() {
            this.cities = [];
        @action.bound
        add(values) {
            this.cities = values;
    export new Store()
    
  • @obserbale 表示定义一个state值
  • @computed 计算属性,表示根据现有的数据计算出来的衍生值,将函数当作属性(函数的return值)用
  • @action.bound@aciton:表示定义更新state的方法,里面是更新state的逻辑,通过这种方式可以直接通过this.xxx来修改state
  • 2.1 含异步操作的action

    mobx不允许在@action中的Promise.then或async @aciton等异步操作中直接进行state的数据更新操作

    官方解释:

    action 包装/装饰器只会对当前运行的函数作出反应,而不会对当前运行函数所调用的函数(不包含在当前函数之内)作出反应! 这意味着如果 action 中存在 setTimeout、promise 的 then 或 async 语句,并且在回调函数中某些状态改变了,那么这些回调函数也应该包装在 action 中。

    一种可行的做法是针对Promise的then回调中修改state的部分也封装成一个@aciton,则调用该aciton而不是在回调中直接修改则可以解决该问题

    推荐的做法是使用mobx提供的runInAction来修改state:

    Promise.then中:

    class Store {
        @observable githubProjects = []
        @observable state = "pending" // "pending" / "done" / "error"
        @action
        fetchProjects() {
            this.githubProjects = []
            this.state = "pending"
            fetchGithubProjectsSomehow().then(
                projects => {
                    const filteredProjects = somePreprocessing(projects)
                    // 将‘“最终的”修改放入一个异步动作中
                    runInAction(() => {
                        this.githubProjects = filteredProjects
                        this.state = "done"
                error => {
                    // 过程的另一个结局:...
                    runInAction(() => {
                        this.state = "error"
    

    async的action中:

     @action
        async fetchProjects() {
            this.githubProjects = []
            this.state = "pending"
            try {
                const projects = await fetchGithubProjectsSomehow()
                const filteredProjects = somePreprocessing(projects)
                // await 之后,再次修改状态需要动作:
                runInAction(() => {
                    this.state = "done"
                    this.githubProjects = filteredProjects
            } catch (error) {
                runInAction(() => {
                    this.state = "error"
    

    2.2 @action.bound和@aciton的区别

    官方解释:

    action 装饰器/函数遵循 javascript 中标准的绑定规则。 但是,action.bound 可以用来自动地将动作绑定到目标对象(即当前Store的实例)。

    注意: action.bound 不要和箭头函数一起使用;箭头函数已经是绑定过的并且不能重新绑定。

    示例如下:

    2.3 页面使用mobx

    在需要的页面中,引入对应Store的实例,并使用mobx-react的@observer进行数据监听,以实现数据的展示以及数据更新来驱动试图的更新

    Store.js

    import {observable, computed, action} from "mobx";
     class Store{
        @observable cities = ["北京","上海"];
         @observable name = "scw";
        @computed
        get total() {
            return this.cities.length;
        @action.bound
        clearCities() {
            this.cities = [];
        @action.bound
        add(values) {
            this.cities = values;
    export new Store()
    

    页面组件:

    import { observer, inject } from "mobx-react"
    import Store from "./Store"
    @observer
    class Page extends Component {
      componentDidMount() {
        Store.add("南京")
      render() {
        return (
              {Store.name}
              {Store.ciies.map(item=><span>{item}</span>)}
            </div>
    export default Page
    

    2.3 mobx的全局注入

    mobx不会强制像redux那样必须全局注入store,如2.2所示,在mobx中,我们可以针对每个特定的页面或者功能编写专有的store来使用。

    不过它也可以在全局注入单个或多个Store来供全局页面组件使用。一般用于一些公共状态或公共方法的设置

    所有store的统一管理文件:

    你可以在这里引入一些公共的Store,统一封装成对象

    import City from "./city";
    export default {
        city: new City()
    

    顶层组件:

    这里引入mobx-react的Providr进行公共Store的全局注入:

    import React from "react";
    import {render} from "react-dom";
    import {Provider} from "mobx-react";
    import {Router, browserHistory, hashHistory} from "react-router";
    import { RouterStore, syncHistoryWithStore } from "mobx-react-router";
    import routes from "./routes";
    import store from "./mobstore";
    const routingStore = new RouterStore();
    const history = syncHistoryWithStore(hashHistory, routingStore);
    store.routing = routingStore;
    render(
        <Provider {...store}>
            <Router history={history} routes={routes}/>
        </Provider>,
        document.querySelector(".container")
    

    组件使用全局注入的Store:

    import React, {Component} from "react";
    import {observer, inject} from "mobx-react";
    @inject("city") 
    @observer
    export default class CitySelector extends Component {
        constructor(...props) {
            super(...props);
            this.state = {
                city: []
       onChange = (value) => {
            this.props.city.add(value);
        clearCities() {
            this.props.city.clearCities();
        render() {
            let { cities, total } = this.props.city;
    

    使用@inject("xxx")来调用公共Store对象中指定属性名的Store,此时该组件可通过this.props.xxx来访问对应的Store。包括里面的数据和方法即@observable和@aciton

    3. Redux vs Mobx

    (1)单一store和多store

    store是应用管理数据的地方,在Redux应用中,我们总是将所有共享的应用数据集中在一个大的store中,

    而Mobx则通常按模块将应用状态划分,在多个独立的store中管理。

    (2)JavaScript对象和可观察对象

    Redux默认以JavaScript原生对象形式存储数据,而Mobx使用可观察对象:

  • Redux需要手动追踪所有状态对象的变更;
  • Mobx中可以监听可观察对象,当其变更时将自动触发监听;
  • (3)不可变(Immutable)和可变(Mutable)

    Redux状态对象通常是不可变的(Immutable),我们不能直接操作状态对象,而总是在原来状态对象基础上返回一个新的状态对象,这样就能很方便的返回应用上一状态;

    而Mobx中可以直接使用新值更新状态对象。

    (4)mobx-react和react-redux

    使用Redux和React应用连接时,需要使用react-redux提供的Provider和connect:

  • Provider:负责将Store注入React应用;
  • connect:负责将store state注入容器组件,并选择特定状态作为容器组件props传递;
  • 对于Mobx而言,同样需要两个步骤:

  • Provider:使用mobx-react提供的Provider将所有stores注入应用;
  • 使用inject将特定store注入某组件,store可以传递状态或action;然后使用observer保证组件能响应store中的可观察对象(observable)变更,即store更新,组件视图响应式更新。
  • 选择Mobx的原因

    学习成本少:Mobx基础知识和配置很简单,而Redux确较繁琐,流程较多,需要配置,创建store,编写reducer,action,如果涉及异步任务,还需要引入redux-thunk或redux-saga编写额外代码,Mobx流程相比就简单很多,并且不需要额外异步处理库;