引入 Mobx 使用和介绍

发布于 2022-12-03 12:35:27 字数 5770 浏览 114 评论 0

跟 Redux 相比

  • 函数式 VS 面向对象
  • redux 需要 connect,也需要 Immutable Data,reducer,action,文件、代码量较多,概念也多。 mobx 直接引用对象组织,修改数据。
  • redux 数据流动很自然,任何 dispatch 都会导致广播,需要依据对象引用是否变化来控制更新粒度。mobx 数据流流动不自然,只有用到的数据才会引发绑定,局部精确更新,但免去了粒度控制烦恼。
  • redux 有时间回溯,每个 action 都被记录下来,可预测性,定位错误的优势。mobx 只有一份数据引用,不会有历史记录。
  • redux 引入中间件去解决异步操作,以及很多复杂的工作。mobx 没有中间件,数据改了就是改了,没有让你增加中间件的入口。

为什么用 mobx

  • 简单,概念,代码少
  • class 去定义、组织 store,数据、computed、action 定义到一块,结构更清晰,面向对象的思维更适合快速的业务开发
  • 某个 store 的引用不一定非在组件中才能取到,因为是对象,可以直接引用。比如在 constant.js 文件中可以定义一些来自 store 的变量。
  • 据说效率更高。mobx 会建立虚拟推导图 (virtual derivation graph),保证最少的推导依赖

mobx 概念

flow
上图是 mobx 结合 react 使用的数据流图。

Observable state

给数据对象添加可观测的功能,支持任何数据结构。

class State {
  @observable price = 10;
}

Computed values

某个 state 发生变化时,需要自动计算的值。比如说单价变化,总价的计算

class State {
  @observable price = 10;
  @observable count = 0;
  @computed get total() {
    return price * count;
  }
}

Reactions

Reactions 和 Computed 类似,都是 state 变化后触发。但它不是去计算值,而是会产生副作用,比如 console、网络请求、react dom 更新等。mobx 提供了三个函数用于自定义 reactions。

const state = new State;
autorun(() => {
  console.log("Current Price : " + state.price);
})

每当 state 中的单价 price 发生变化,控制台都会打印。注意这里 state.count 变化是不会执行的。

react 组件 reactions。利用 mobx-react 中的 observer 对 react 组件进行包装。

import React, {Component} from 'react';
import {observer} from "mobx-react";

const View = (price, count) => <div>
  {price * count}
</div>

@observer
class Container extends Component {
  render() {
    return (
      <div>
        <Input />
        <View 
          price={state.price} 
          count={state.count}
        />
      </div>
    )
  }
}

Actions

个人觉得 mobx 中的 action 不像 redux 中是必需的,算是我们把一些修改 state 的操作都规范的用 action 标注,并且可以描述这个 action。随意的 state.price = 5 更改 state 都是可以起到作用的,只不过这样的话会很乱,你完全不知道哪里的操作引起了 state 的变化,所以 mobx 是建议你对 state 的副作用操作,都用 @aciton 去装饰。

class State {
  @observable price = 10;
  @observable count = 0;
  @computed get total() {
    return price * count;
  }
  @action priceChange(val) {
    this.price = val;
  }
  @action countChange(val) {
    this.count = val;
  }
}
const state = new State;

class Input extends Component {
  handleChange = type => e => {
    const value = e.target.value;
    this.props[`${type}Change`](value);
  }
  render() {
    return (
      <div>
        价格:<input type='number' onChange={this.handleChange('price')} />
        数量:<input type='number' onChange={this.handleChange('count')} />
      </div>
    )
  }
}
const View = (total) => <div>
  {total}
</div>

@observer
class Container extends Component {
  render() {
    const { total, priceChange, countChange } = state;
    return (
      <div>
        <Input 
          priceChange={priceChange}
          countChange={countChange}
        />
        <View 
          total={total}
        />
      </div>
    )
  }
}

ps: 以上代码我都没跑过,有任何问题概不负责

异步 action

只需要把异步操作、请求也放到 @action 里就好了。假设我们已经封装好了一个 fetch 的方法,并返回一个 promise,现在去使用一个异步 action 请求 list 数据

  @observable list = [
    loading: false,
    dataSource: [],
  ];
  @action getList = async () => {
    this.list.loading = true;
    const data = await fetch();
    this.list.dataSource = data;
  }

常见问题

observable 之后的数组并不是普通数组的形式,所以有时候在组件内部做判断的时候可能会有问题,通常用 toJS() 转化一下。

observer 不要放到顶层 Page,因为当随便一个 state 的属性都改变,整个 Page 都会 render,即使其他 children 组件的 dom 结构没变,但还是会有一些性能开销的。所以 observer 尽量去包装小组件。

与自定义的 hoc 连用的时候,observer要 放到最里面。因为要包装组件的 render 函数,还要收集 state 的依赖。形如

@inject('store')
@myHOC
@observer
class A extends Component {
    
}

如果 observer 在外层,state 改变组件是不会做相应的。

不必像 redux 那样,把所有的 store 都注入 Provider, 虽然 mobx 支持这么做。一些公用的 store,比如用户信息啦,可以注入 Provider,然后子组件通过 inject 注入。但是大部分不能公用的列表数据,可以直接 import

import listStore from 'stores/list';

@observer class List extends Component {
  render() {
    return (
      <ul>
        {listStore.list.map(item => <li>{item.name}</li>)}
      </ul>
    )
  }
}

mobx 和 react 的 state

使用了 mobx,你会发现 setState 那种写法以及不能立刻生效是很不习惯的。大部分情况下,你都可以不去写 state,而是通过 observable 去定义变量,observer 包装组件,这些变量也可以定义到组件内部(可观察的局部组件状态)。例如:

import {observer} from "mobx-react"
import {observable} from "mobx"

@observer class Count extends React.Component {
  @observable num = 0

  componentDidMount() {
    setInterval(() => {
      this.num++
    }, 1000)
  }

  render() {
    return <span>Count: { this.num } </span>
  }
})

其他,参照官方文档中文链接

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

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

发布评论

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

关于作者

小女人ら

暂无简介

0 文章
0 评论
23 人气
更多

推荐作者

lorenzathorton8

文章 0 评论 0

Zero

文章 0 评论 0

萧瑟寒风

文章 0 评论 0

mylayout

文章 0 评论 0

tkewei

文章 0 评论 0

17818769742

文章 0 评论 0

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