MobX 上手指南

巴拉米
• 阅读 1157

之前用 Redux 比较多,一直听说 Mobx 能让你体验到在 React 里面写 Vue 的感觉,今天打算尝试下 Mobx 是不是真的有写 Vue 的感觉。

题外话

在介绍 MobX 的用法之前,先说点题外话,我们可以看一下 MobX 的中文简介。在 MobX 的中文网站上写着:

MobX 是一个经过战火洗礼的库,它通过透明的函数响应式编程使得状态管理变得简单和可扩展。

MobX 上手指南

“战火洗礼的库” 怎么看都感觉很奇怪,读起来很拗口😂,而且网上很多介绍 MobX 的文章都是这么写的,在 github 翻阅其 README 发现写的是:

MobX is a battle tested library that makes state management simple and scalable by transparently applying functional reactive programming (TFRP).

可以看到作者原本要表达的意思是 MobX 是经过了许多的测试,拥有比较强的健壮性。下面是通过谷歌翻译的结果,看起来也比中文网的表达要准确一些。

MobX 上手指南

虽然,我的英文水平也很菜,还是会尽量看官方的文档,这样可以避免一些不必要的误解。

如何使用?

言归正传,MobX 现在的最新版是 6.0,这个版本的 API 相比于之前有了极大的简化,可以说更加好用了。之前的版本是装饰器风格的语法糖,但是装饰器在现在的 ES 规范中并不成熟,而且引入装饰器语法也在增加打包后的代码体积。综合考虑后,MobX 6.0 取消了装饰器语法的 API。

响应式对象

MobX 通过 makeObservable 方法来构造响应式对象,传入的对象属性会通过 Proxy 代理,与 Vue 类似,在 6.0 版本之前使用的是 Object.defineProperty API,当然 6.0 也提供了降级方案。

import { configure, makeObservable, observable, action, computed } from 'mobx'

// 使用该配置,可以将 Proxy 降级为 Object.defineProperty
configure({ useProxies: "never" });

// 构造响应对象
const store = makeObservable(
  // 需要代理的响应对象
  {
    count: 0,
    get double() {
      return this.count * 2
    },
    increment() {
      this.count += 1
    },
    decrement() {
      this.count -= 1
    }
  },
  // 对各个属性进行包装,用于标记该属性的作用
  {
    count: observable, // 需要跟踪的响应属性
    double: computed,  // 计算属性
    increment: action, // action 调用后,会修改响应对象
    decrement: action, // action 调用后,会修改响应对象
  }
)

我们在看看之前版本的 MobX,使用装饰器的写法:

class Store {
  @observable count = 0
  constructor() {
    makeObservable(this)
  }
  @action increment() {
    this.count++;
  }
  @action decrement() {
    this.count--;
  }
  @computed get double() {
    return this.count * 2
  }
}

const store = new Store()

这么看起来,好像写法并没有得到什么简化,好像比写装饰器还要复杂点。下面我们看看 6.0 版本一个更强大的 API:makeAutoObservable

makeAutoObservable 是一个更强大的 makeObservable,可以自动为属性加上对象的包装函数,上手成本直线下降。

import { makeAutoObservable } from 'mobx'

const store = makeAutoObservable({
  count: 0,
  get double() {
    return this.count * 2
  },
  increment() {
    this.count += 1
  },
  decrement() {
    this.count -= 1
  }
})

计算属性

MobX 的属性与 Vue 的 computed 一样,在 makeAutoObservable 中就是一个 gettergetter 依赖的值一旦发生变化,getter 本身的返回值也会跟随变化。

import { makeAutoObservable } from 'mobx'

const store = makeAutoObservable({
  count: 0,
  get double() {
    return this.count * 2
  }
})

store.count 为 1 时,调用 store.double 会返回 2。

修改行为

当我们需要修改 store 上的响应属性时,我们可以通过直接重新赋值的方式修改,但是这样会得到 MobX 的警告⚠️。

const store = makeAutoObservable({
  count: 0
});

document.getElementById("increment").onclick = function () {
  store.count += 1
}

MobX 上手指南

MobX 会提示,在修改响应式对象的属性时,需要通过 action 的方式修改。虽然直接修改也能生效,但是这样会让 MobX 状态的管理比较混乱,而且将状态修改放到 action 中,能够让 MobX 在内部的事务流程中进行修改,以免拿到的某个属性还处于中间态,最后计算的结果不够准确。

makeAutoObservable 中的所有方法都会被处理成 action。

import { makeAutoObservable } from 'mobx'

const store = makeAutoObservable({
  count: 0,
  get double() {
    return this.count * 2
  },
  increment() { // action
    this.count += 1
  },
  decrement() { // action
    this.count -= 1
  }
})

不同于 Vuex,将状态的修改划分为 mutation 和 action,同步修改放到 mutation 中,异步的操作放到 action 中。在 MobX 中,不管是同步还是异步操作,都可以放到 action 中,只是异步操作在修改属性时,需要将赋值操作放到 runInAction 中。

import { runInAction, makeAutoObservable } from 'mobx'

const store = makeAutoObservable({
  count: 0,
  async initCount() {
    // 模拟获取远程的数据
    const count = await new Promise((resolve) => {
      setTimeout(() => {
        resolve(10)
      }, 500)
    })
    // 获取数据后,将赋值操作放到 runInAction 中
    runInAction(() => {
      this.count = count
    })
  }
})

store.initCount()

如果不调用 runInAction ,则可以直接调用本身已经存在的 action。

import { runInAction, makeAutoObservable } from 'mobx'

const store = makeAutoObservable({
  count: 0,
  setCount(count) {
    this.count = count
  },
  async initCount() {
    // 模拟获取远程的数据
    const count = await new Promise((resolve) => {
      setTimeout(() => {
        resolve(10)
      }, 500)
    })
    // 获取数据后,调用已有的 action
    this.setCount(count)
  }
})

store.initCount()

监听对象变更

无论是在 React 还是在小程序中想要引入 MobX,都需要在对象变更的时候,通知调用原生的 setState/setData 方法,将状态同步到视图上。

通过 autorun 方法可以实现这个能力,我们可以把 autorun 理解为 React Hooks 中的 useEffect。每当 store 的响应属性发生修改时,传入 autorun 的方法(effect)就会被调用一次。

import { autorun, makeAutoObservable } from 'mobx'

const store = makeAutoObservable({
  count: 0,
  setCount(count) {
    this.count = count
  },
  increment() {
    this.count++
  },
  decrement() {
    this.count--
  }
})

document.getElementById("increment").onclick = function () {
  store.count++
}

const $count = document.getElementById("count")
$count.innerText = `${store.count}`
autorun(() => {
  $count.innerText = `${store.count}`
})

每当 button#increment 按钮被点击的时候,span#count 内的值就会自动进行同步。👉查看完整代码

MobX 上手指南

除了 autorun ,MobX 还提供了更精细化的监听方法:reactionwhen

const store = makeAutoObservable({
  count: 0,
  setCount(count) {
    this.count = count
  },
  increment() {
    this.count++
  },
  decrement() {
    this.count--
  }
})

// store 发生修改立即调用 effect
autorun(() => {
  $count.innerText = `${store.count}`
});

// 第一个方法的返回值修改后才会调用后面的 effect
reaction(
  // 表示 store.count 修改后才会调用
  () => store.count,
  // 第一个参数为当前值,第二个参数为修改前的值
  // 有点类似与 Vue 中的 watch
  (value, prevValue) => {
    console.log('diff', value - prevValue)
  }
);

// 第一个方法的返回值为真,立即调用后面的 effect
when(() => store.count > 10, () => {
  console.log(store.count)
})
// when 方法还能返回一个 promise
(async function() {
  await when(() => store.count > 10)
  console.log('store.count > 10')
})()

总结

MobX 的介绍到这里就结束了,本文只是大致的列举了一下 MobX 的 API,希望大家能有所收获。后续打算再深入研究下 MobX 的实现,等我研究好了,再写篇文章来分享。

MobX 上手指南

点赞
收藏
评论区
推荐文章
秃头王路飞 秃头王路飞
6个月前
webpack5手撸vue2脚手架
webpack5手撸vue相信工作个12年的小伙伴们在面试的时候多多少少怕被问到关于webpack方面的知识,本菜鸟最近闲来无事,就尝试了手撸了下vue2的脚手架,第一次发帖实在是没有经验,望海涵。languageJavaScript"name":"vuecliversion2","version":"1.0.0","desc
浅梦一笑 浅梦一笑
6个月前
初学 Python 需要安装哪些软件?超级实用,小白必看!
编程这个东西是真的奇妙。对于懂得的人来说,会觉得这个工具是多么的好用、有趣,而对于小白来说,就如同大山一样。其实这个都可以理解,大家都是这样过来的。那么接下来就说一下python相关的东西吧,并说一下我对编程的理解。本人也是小白一名,如有不对的地方,还请各位大神指出01名词解释:如果在编程方面接触的比较少,那么对于软件这一块,有几个名词一定要了解,比如开发环
光头强的博客 光头强的博客
6个月前
Java面向对象试题
1、请创建一个Animal动物类,要求有方法eat()方法,方法输出一条语句“吃东西”。创建一个接口A,接口里有一个抽象方法fly()。创建一个Bird类继承Animal类并实现接口A里的方法输出一条有语句“鸟儿飞翔”,重写eat()方法输出一条语句“鸟儿吃虫”。在Test类中向上转型创建b对象,调用eat方法。然后向下转型调用eat()方
刚刚好 刚刚好
6个月前
css问题
1、在IOS中图片不显示(给图片加了圆角或者img没有父级)<div<imgsrc""/</divdiv{width:20px;height:20px;borderradius:20px;overflow:h
小森森 小森森
6个月前
校园表白墙微信小程序V1.0 SayLove -基于微信云开发-一键快速搭建,开箱即用
后续会继续更新,敬请期待2.0全新版本欢迎添加左边的微信一起探讨!项目地址:(https://www.aliyun.com/activity/daily/bestoffer?userCodesskuuw5n)\2.Bug修复更新日历2.情侣脸功能大家不要使用了,现在阿里云的接口已经要收费了(土豪请随意),\\和注意
晴空闲云 晴空闲云
6个月前
css中box-sizing解放盒子实际宽高计算
我们知道传统的盒子模型,如果增加内边距padding和边框border,那么会撑大整个盒子,造成盒子的宽度不好计算,在实务中特别不方便。boxsizing可以设置盒模型的方式,可以很好的设置固定宽高的盒模型。盒子宽高计算假如我们设置如下盒子:宽度和高度均为200px,那么这会这个盒子实际的宽高就都是200px。但是当我们设置这个盒子的边框和内间距的时候,那
GoCoding GoCoding
1年前
React MobX 开始
用于状态管理,简单高效。本文将于React上介绍如何开始,包括了:了解MobX概念从零准备React应用MobXReact.FC写法MobXReact.Component写法可以在线体验:https://ikuokuo.github.io/startreact,代码见:https://github.com/ikuokuo/s
艾木酱 艾木酱
5个月前
快速入门|使用MemFire Cloud构建React Native应用程序
MemFireCloud是一款提供云数据库,用户可以创建云数据库,并对数据库进行管理,还可以对数据库进行备份操作。它还提供后端即服务,用户可以在1分钟内新建一个应用,使用自动生成的API和SDK,访问云数据库、对象存储、用户认证与授权等功能,可专
密钥管理系统-为你的天翼云资产上把“锁
本文关键词:数据安全,密码机,密钥管理一、你的云上资产真的安全么?1.2021年1月,巴西的一个数据库30TB数据被破坏,泄露的数据包含有1.04亿辆汽车和约4000万家公司的详细信息,受影响的人员数量可能有2.2亿;2.2021年2月,广受欢迎的音频聊天室应用Clubhouse的用户数据被恶意黑客或间谍窃取。据悉,一位身份不明的用户能够将Clubho
NVIDIA安培架构下MIG技术分析
关键词:NVIDIA、MIG、安培一什么是MIG2020年5月,NVIDIA发布了最新的GPU架构:安培,以及基于安培架构的最新的GPU:A100。安培提供了许多新的特性,MIG是其中一项非常重要的新特性。MIG的全名是MultiInstanceGPU。NVIDIA安培架构中的MIG模式可以在A100GPU上并行运行七个作业。多实
helloworld_28799839 helloworld_28799839
5个月前
常用知识整理
Javascript判断对象是否为空jsObject.keys(myObject).length0经常使用的三元运算我们经常遇到处理表格列状态字段如status的时候可以用到vue