这也许也是你成长的模样 -- Mobx

884 阅读3分钟

前言

你知道什么才是大佬吗?浩某正思考着这一花里胡哨的问题,突然被刘某打断:“哎,你过来帮我看个问题”。

说罢,便前往他的电脑前查看问题,是这样的,我在 Mobx 里定义的数据然后我把他赋值到 State 上之后 State 上的值改变了,为啥 Mobx 的没有改变?

浩某放下了手中的咖啡说道:“你在 State 上只是初始化赋值而已,相当于 this.state = { a: 1 } 他就是个初始化的值”。

“这样吗?”

刘某随即答道然后便又开始码了起来。

“其实大佬就是比你知道的东西多而已,仅此而已”

浩某突然萌生起了这个片面的想法。

随即便翻起了 Mobx 的官方文档看了起来。

Mobx

MobX 是一个经过战火洗礼的库,它通过透明的函数响应式编程(transparently applying functional reactive programming - TFRP)使得状态管理变得简单和可扩展。MobX 背后的哲学很简单:

任何源自应用状态的东西都应该自动地获得。

其中包括UI、数据序列化、服务器通讯,等等。

更多请查阅:官方文档

看到一半,浩某随即抄起键盘就开始码例子以便加深印象(因为装饰器只能应用于类所以用类写法、Mobx 版本 4.x):

// goodsStore.js
// 定义可观测状态以及改变状态的动作
import { action, computed, observable } from 'mobx'
class goodsStore {
    @observable defaultActiveKey = '1'
    
    @action setDefaultActiveKey(val) {
        this.defaultActiveKey = val
    }
}

export default goodsStore

// RootStore.js
// 你可能有很多模块将他汇总成一个 js 文件并导出
import goodsStore from './goodsStore'
// import so on ...

class RootStore {
  constructor() {
    this.goodsStore = new goodsStore()
    // init store ...
  }
}

export default new RootStore()
// main.jsx
// 在根组件下使用并传导 RootStore
import RootStore from './RootStore.js'
import { Provider } from 'mobx-react'

class App extends React.Component {
    constructor(props) {
        super(props)
        // do something ...
    }
    render() {
        <Provider {...RootStore}>
            // do something ...
            // include what you want ...
            // your router and so on ...
        </Provider>
    }
}

// app.jsx
// 使用 mobx
import React from 'react'
import { inject, observer } from 'mobx-react'
import { Tabs } from 'antd'

const { TabPane } = Tabs

@inject('Store')
@observer
class App extends React.Component {
    constructor(props) {
        super(props)
        this.state = {
          defaultActiveKey: this.props.goodsStore.defaultActiveKey,
        }
    }
    onChange(e) {
        this.props.goodsStore.setDefaultActiveKey(e)
    }
    render() {
        const { defaultActiveKey } = this.state
        return (
            <div>
                <Tab
                    defaultActiveKey={defaultActiveKey}
                    onChange={this.onChange.bind(this)}
                >
                    <TabPane tab="商品1" key="1"></TabPane>
                    <TabPane tab="商品2" key="2"></TabPane>
                    <TabPane tab="商品3" key="3"></TabPane>
                </Tab>
            </div>
        )
    }
}

看到用法相较于 Redux 确实灵活不少,同时用起来又颇有 Vuex 的味道,浩某露出了满足的笑容。

正当浩某码的正香,却突然被这段代码困住:

// goodsStore.js
// 定义可观测状态以及改变状态的动作
import { action, computed, observable } from 'mobx'
class goodsStore {
    @observable defaultActiveKey = '1'
    
    // 这里
    @observable goodsList = []
    
    @action setDefaultActiveKey(val) {
        this.defaultActiveKey = val
    }
    
    // 这里
    @action setGoodsList(val) {
        this.goodsList = val
    }
    
}

export default goodsStore

浩某发现这个被观测的数组居然不是一个数组即 Array.isArray(observable([])) = false, 随即查阅文档发现,原来由于 ES5 的局限性, Mobx 会创建一个类数组对象来代替真正数组进行操作且支持所有原生方法。所以官方给的建议是通过 array.slice() 在传递到外部之前创建一份浅拷贝。这样Array.isArray(observable([]).slice())将为 true,以方便进行其他的数组操作等。

emm...

这看上去有点多余,实际上我定义一个可观测数组还需要再多定义一个向外暴露的数组以确保他不会出其他的意外:

@computed get goodsListSlice() {
    return this.goodsList && this.goodsList.length > 0 ?
    this.goodsList.slice() : []
}

Mobx 与 Redux

关于 Redux 在往期文章中有详述:往期文章

关于两者

  • Mobx 是基于双向绑定的响应式实现,而 Redux 是基于 Flux的单向数据流实现。

  • Redux 中数据是只读的;Mobx 中的数据可读可写,并且 action 非必须,可直接改变。

  • Redux 的维护性比 Mobx 强,这一点主要基于他的思想:清晰的单向数据流。而 Mobx 基于上述第二条,导致他的数据流并不很清晰。

  • Redux 是单一数据源;而 Mobx 是多个 store。

  • 从代码量上看,Mobx 能少写很多代码,而 Redux 要通过 action, reducer 等等的编写才能实现整个流程。

Mobx 也好,Redux 也好,主要看是否契合当前项目框架。

浩某最后停下键盘,将梳理好的知识点存入有道云笔记以便随时复盘。

最后

以上故事纯属虚构。都看到这了不点个赞吗?

感谢各位大佬的收看,欢迎在下面吐槽(Q A Q)