Skip to content

Latest commit

 

History

History
170 lines (145 loc) · 5.04 KB

3.mobx在实际工程的范式总结.md

File metadata and controls

170 lines (145 loc) · 5.04 KB

mobx在实际工程的范式总结

mobx 使用灵活,官方亦有相关的使用范式,但我这边依旧不能适应那种较为繁杂的用法(过于繁杂的用法会降低团队 协同效率并提高维护成本)。所以这里自己总结了一套范式,可供参考。

store 的构建

基于 class 并对 class 属性增加 observable 修饰来构建用于储存状态的 store,当需要对 store 进行拓展时,则 单独构建一个特定的 store 并将其添加到主 store 中去,将最终的 store 构建为一个类似于 json 格式的对象。

主 store:

// 主 store 类
class Store {
    @observable userInfo;
    @observable pageInfo;

    /**
     * 全局状态的初始化(类似与reducer的组装工作)
     */
    constructor (props) {
        this.userInfo = new UserStore(props.userInfo);
        this.pageInfo = new PageStore(props.pageInfo);
    }
}

// 主 sotre (初始化)
const store = new Store({
    userInfo: {
        username: '',
        nickname: '',
        email: ''
    },
    pageInfo: {
        title: 'index',
        subTitle: 'sub'
    }
});

pagestore:

// 用于存储 pageInfo 的 store 类(userInfo 的 store 与之类似)
class PageStore {
    @observable title;
    @observable subTitle;

    @computed get content () {
        return `${ this.title }-${ this.subTitle }`;
    }

    /**
     * 页面状态信息初始化
     */
    constructor (pageInfo) {
        this.title = pageInfo.title;
        this.subTitle = pageInfo.subTitle;
    }
}

按照这样的范式即可很方便的构建与拓展 store 来做为页面状态的存储与管理。

computed 的规范

computed 的规范是只能用于整合被 observable 修饰的变量(拼接、拆分、正则替换等,但不能修改相应变量的原始值),且需 要有返回值,不能在里面进行异步的操作。(有点类似于数据库中 view 的概念)。

引入 action

一般来说在 mobx 中可以直接修改上述 store 中的值来实现状态变更(修改后触发 autorun 中的函数),但这样的做的话会导 致状态变更变得混乱难以管理,所以在本范式中强制只能使用 action 来修改 store。为了保证强制使用 action 在主 store 定义的地方也需要加上 useStrict(true) 。(actionuseStrict 均引自 mobx)。

import { action, runInAction } from 'mobx';

import store from './store';

/**
 * actions 操作
 * @static
 */
class Actions {
    /**
     * 修改用户名(同步)
     * @param {String} username - 用户名 
     */
    @action('change username') static changeUsername (username) {
        store.userInfo.username = username;
    }

    ...
}

// 触发 change username 的 action
Actions.changeUsername('new username');

@action 后的 change username 为该 aciton 的名称,若发生报错该名称亦会显示方便追踪。

异步方案

由于 action 修饰的函数只能涉及该函数 stack 中有关 store 的修改,如果有异步操作,以下的形式并不是规范做法,ex:

class Actions {
    /**
     * 修改用户名(异步,useStrict 会报错)
     * @param {String} username - 用户名
     * @param {Function} callback - 回调
     */
    @action('change username') static changeUsername (username, callback) {
        // 由于 action 只能修饰到该层 stack,在异步中的部分没有计入其中,在 useStrict(true) 时,即使使
        // 用该 action 修改 username,仍会判定为未使用 action,导致报错。
        setTimeout('change username', () => {
            store.userInfo.username = username;
            return callback();
        }, 2000);
    }

    ...
}

这时可以使用 runInAction 进行解决:

class Actions {
    /**
     * 修改用户名(异步)
     * @param {String} username - 用户名
     * @param {Function} callback - 回调
     */
    @action('change username') static changeUsername (username, callback) {
        setTimeout(() => {
            // 采用 runInAction 直接执行包含的函数,可被视为通过 action 操作
            runInAction('change username', () => {
                store.userInfo.username = username;
                return calback();
            });
        }, 2000);
    }

    ...
}

也可以:

class Actions {
    /**
     * 修改用户名(同步)
     * @param {String} username - 用户名 
     */
    @action('change username') static _changeUsername (username) {
        store.userInfo.username = username;
    }

    /**
     * 修改用户名(异步)
     * @param {String} username - 用户名
     * @param {Function} callback - 回调
     */
    static changeUsername (username, callback) {
        setTimeout(() => {
            Actions._changeUsername(username);
            return callback();
        }, 2000);
    }

    ...
}

详细代码参考:/scripts/base3 目录,其中的 index.js,可以直接用 babel-node 运行。