解读 Mobx 及与 redux 的对比

发布于 2022-10-11 21:18:34 字数 7835 浏览 147 评论 0

众所周知,在 react 生态中,状态管理工具除了 redux,比较常用的就是 mobx 了。所以今天将会初略的写一下mobx的用法,总结mobx的使用场景(技术选型是一件比较有挑战的事,需要根据实际的应用场景来决定)。

mobx 的 API 非常的简洁,需要了解的内容并不多,如果在 react 中使用,需要借助 mobx-react

首先在项目中安装 mobx 和 mobx-react:

npm i --save-D mobx mobx-react

mobx 中会大量使用装饰器,如果使用ES,可以借助babel来使用ES7中的装饰器特性;这里更推荐Typescript,可以使用"experimentalDecorators": true来开启对装饰器的支持,同时借助TSX,在react使用Typescript体验会非常的棒。

mobx 的流程图如上,通常是:触发 action,在 action 中修改 state,通过computed拿到state的计算值,自动触发对应的reactions,这里包含autorun,渲染视图等。有一点需要注意:相对于react来说,mobx没有一个全局的状态树,状态分散在各个独立的store中。mobx的工作原理非常简单,使用Object.defineProperty来拦截对数据的访问,一旦值发生变化,将会调用react的render方法来实现重新渲染视图的功能或者触发autorun等。

下面看几个示例,我会尽量在示例代码中包含多个概念(同时这里使用了 Typescript,如果不熟悉,可以看一下 这个),下面是第一个例子:

import * as React from 'react';
import { observable, action, autorun, when, computed } from 'mobx';
import { observer } from 'mobx-react';

@observer
export default class Item extends React.Component {
  disposer: () => void;

  @observable
  count: string = 'right';

  @action
  setCount = () => {
    this.count = this.count === 'right' ? 'wrong' : 'right';
  }

  cancelAutoRun = () => {
    if (this.disposer) {
      this.disposer();
    }
  }

  @computed
  get helloCount() {
    return `hello ${this.count}`;
  }

  render() {
    return(
      <div>
      <h3>{this.count}</h3>
      <h4>{this.helloCount}</h4>
      <button onClick={this.setCount}>click</button>
      <button onClick={this.cancelAutoRun}>cancel</button>
      </div>);
  }

  componentDidMount() {
    this.disposer = autorun(() => {
      console.log(this.count);
    });

    when(
      () => this.count === 'wrong',
      () => console.log(this.count)
    );
  }
}

上面这段代码很简单:

  1. 通过observable装饰器装饰一个属性,这个属性变动之后,就会自动触发响应的动作,这里的属性可以是string,boolean,array,object等
  2. autorun,when,observer都是reactions,当observable装饰的可观察属性发生变化时,会触发它们自动执行
  3. observer的作用是,当可观察属性发生变化时,调用react组件的render方法重新渲染视图
  4. autorun,当autorun函数中依赖的可观察属性发生变化时,就会自动触发autorun函数的执行,同时,autorun函数返回一个函数,调用该函数将会在执行期间清理 autorun。
  5. when,该函数接受两个函数作为参数,第一个函数返回一个判断条件;在该判断条件满足时,将会执行第二个函数;when函数只会执行一次。
  6. computed的行为跟vue中的computed行为一直,它与autorun的区别简单的说,computed需要被使用才会触发自动计算,被使用可以是在视图中渲染这个值,也可以是其他reactions

然后再看另一个例子:

import * as React from 'react';
import { observable, intercept, observe } from 'mobx';
import { observer } from 'mobx-react';
import { ChangeEvent } from 'react';

@observer
export default class Item extends React.Component {

  @observable
  apple = {
    name: 'apple'
  };

  disposer1: () => void;

  disposer2: () => void;

  handleInput = (event: ChangeEvent<HTMLInputElement>) => {
    this.apple.name = event.target.value;
  }

  render() {
    return (
      <div>
        <h3>{this.apple.name}</h3>
        <input type="text" onChange={this.handleInput}/>
        </div>
    );
  }
  componentDidMount() {
    this.disposer1 = intercept(this.apple, 'name', (change) => {
      if (change.newValue === 'hello') {
        this.disposer1();
      }
      change.newValue = 'hi' + change.newValue;
      return change;
    });

    this.disposer2 = observe(this.apple, 'name', (change) => {
      console.log(change.newValue);
      console.log(change.oldValue);
    });
  }
}

这段示例代码有如下需要注意的点:

  1. observe/intercept都能拦截变化值(注意这里是observe,不是observer)
  2. 他们都会返回一个函数,执行函数将会清理掉对应的observe或者intercept
  3. intercept会在observe之前执行,可以在intercept中对可观察的值进行修改,修改后的值会反映到视图中去,也可以返回null,那么对应值的修改将不会触发对应的reactions
  4. observe中不能修改可观察的值,对应的回调函数中能够拿到oldValue和newValue,使用方法跟vue中的watch类似

接下来是第三个例子:

// apple.ts
import { action, observable } from 'mobx';

class Apple {
  @observable
  name: string = '';

  @action
  async setName(value: string) {
    const result = await new Promise((resolve) => {
      setTimeout(() => {
        resolve(value);
      }, 4000);
    });
    this.name = result as string;
  }
}
export default new Apple();


//Five.tsx

import * as React from 'react';
import { observer } from 'mobx-react';
import apple from './apple';

@observer
export default class Five extends React.Component {
  apple = apple;

  setName = () => {
    this.apple.setName('hello world');
  }
  render() {
    return (
      <div>
      {this.apple.name}
      <button onClick={this.setName}>change</button>
    </div>);
  }
}

这其中有一下需要注意的点:

  1. 相较于redux中使用middleware来处理异步,mobx中不需要那么复杂,只需要使用async/await来优雅的处理异步的逻辑
  2. mobx中推荐使用单例模式来管理各种数据流,就像上面的Apple类,封装了可观察值的属性以及对应的操作逻辑,然后new一个实例并导出
  3. 在react中使用mobx,可以不需要操作state或props,直接赋值给类的属性即可

最后一个例子是这样:

import * as React from 'react';
import { inject, observer, Provider } from 'mobx-react';
import { observable } from 'mobx';
import { ChangeEvent } from 'react';

interface CommonProps {
}

interface ContextProps extends CommonProps {
  color: string;
}

@inject('color')
class Message extends React.Component<CommonProps> {
  get  contextProps() {
    return this.props as ContextProps;
  }
  render() {
    return (
      <div>{this.contextProps.color}</div>
    );
  }
}

const MessageWrap => <Message/>;

@observer
export default class Container extends React.Component {
  @observable
  color: string = 'red';

  changeColor = (event: ChangeEvent<HTMLInputElement>) => {
    this.color = String(event.target.value) as string;
  }
  render() {
    return (
      <Provider color={this.color}>
        <div className="name">
          <MessageWrap/>
          <input onChange={this.changeColor}/>
        </div>
      </Provider>
    );
  }
}

这个例子其实使用了mobx中的 Provider 和 inject,其利用了react中的context API,能够实现跨组件传递数据的功能,其中的Message组件中有一个contextProps属性。

上面的内容简短的介绍了mobx的api和使用方式,mobx的api比较简洁,需要了解的不多。接下来将分析一下mobx的适用场景以及和redux的对比。

首先是 redux 和 mobx 的对比:

  1. redux 将数据保存在单一的 store 中,mobx 将数据保存在分散的多个 store 中
  2. redux 使用 plain object 保存数据,需要手动处理变化后的操作;mobx 适用 observable 保存数据,数据变化后自动处理响应的操作
  3. redux 使用不可变状态,这意味着状态是只读的,不能直接去修改它,而是应该返回一个新的状态,同时使用纯函数;mobx 中的状态是可变的,可以直接对其进行修改
  4. mobx 相对来说比较简单,在其中有很多的抽象,mobx更多的使用面向对象的编程思维;redux 会比较复杂,因为其中的函数式编程思想掌握起来不是那么容易,同时需要借助一系列的中间件来处理异步和副作用
  5. mobx 中有更多的抽象和封装,调试会比较困难,同时结果也难以预测;而 redux 提供能够进行时间回溯的开发工具,同时其纯函数以及更少的抽象,让调试变得更加的容易

基于以上的对比,通常会有结论说:对于一些简单的,规模不大的应用来说,用 mobx 就足够了。但是是不是一些大的,复杂的应用就不能用 mobx 呢?我觉得不一定。其实如果能够合理的组织代码的结构,理清依赖关系,一些复杂的场景适用 mobx 也是可以的,不过 mobx 目前对 react ssr 的支持还没有 redux 那么完善,这也是急需改善的点。

相较于 redux,mobx 中存在着一些限制:

  1. 它不会分析你的数据中是否存在着循环依赖
  2. 它不会预先假定你的数据结构是 plain object,class 或是任何其他的数据类型

因此,mobx 不能保证用户提供的数据一定能 JSON 序列化,或者能在有限的时间遍历完。所以它更应该被认为是一个数据流管理工具,能够让你以较小的代价构建自己的状态管理架构。能够快捷的再现有的项目中使用,而不需要进行大规模的重写。

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。
列表为空,暂无数据

关于作者

JSmiles

生命进入颠沛而奔忙的本质状态,并将以不断告别和相遇的陈旧方式继续下去。

0 文章
0 评论
84961 人气
更多

推荐作者

醉城メ夜风

文章 0 评论 0

远昼

文章 0 评论 0

平生欢

文章 0 评论 0

微凉

文章 0 评论 0

Honwey

文章 0 评论 0

qq_ikhFfg

文章 0 评论 0

    我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
    原文