首页/文章/javascript

十分钟玩转Mobx(

2023-12-27
22968 分钟
...

当项目越发复杂时,我们发现仅仅是提升状态已经无法适应如此复杂的状态管理了,程序状态变得比较难同步,这意味着我们需要更好的状态管理方式,于是就引入了状态管理库,如 Redux,Mobx 等。本文以Mobx 为主,再重新回顾一下~

mobx的基本概念

1.1 介绍

  • 简单、可扩展的状态管理工具
  • 通过运用透明的函数式响应编程使状态管瘤变得简单和可拓展

1.2 Redux和Mobx的区别

注意:复杂的应用也不是说不能用,需要合理的理清业务逻辑关系,合理的使用。

1.3 版本说明

  • Mobx 4 可以运行在任何支持 ES5 语法的浏览器(Object.defineProperty)
  • Mobx 5 版本运行在任何支持 ES5 语法的浏览器(Proxy)
  • Mobx 4 和 Mobx 5 具有相同的 api,都需要使用装饰器语法
  • Mobx 6 是目前最新版本,为了与标准 javaScript 的最大兼容,默认情况下放弃了装饰器语法。(本文主要介绍 Mobx6)

Mobx的基本使用

2.1 配置环境

  1. 使用 create-react-app 初始化项目
npx create-react-app my-app --template typescript

安装 mobx、mobx-react 或者 Mobx-react-lite【只支持函数组件】

yarn add Mobx Mobx-react --save

2.2 核心概念

  1. observable 定义一个存储 state 的可追踪字段(Proxy)

  2. action 将一个方法标记为可以修改 state 的 action

  3. computed 标记一个可以由 state 派生出新值并且缓存其输出的计算属性

  4. 工作流程

2.3 创建 store

  1. 新建文件 store/Counter.ts, 通过 class 创建一个 Counter 类
  2. 使用 makeObservable 将类的方法 和属性变成响应式的
  3. 导出 counter 实例

注:mobx 中的每一个 store 都应该只初始化一次


import {action, makeObservable, observable} from 'Mobx'
class Counter {
  constructor(){
    
    
    makeObservable(this, {
       count: observable,
       increment: action,
       decrement: action,
       reset: action,
     })
  }
  count = 0
  increment(){
    this.count++
  }
  decrement(){
    this.count--
  }
  reset(){
    this.count = 0
  }
}
const counter = new Counter()
export default counter

2.4 在组件中使用

  • 从 Mobx-react 库中引入 observer 高阶组件函数
  • 使用 observer 高阶组件函数包裹需要使用 store 的组件
  • 引入 store 对象
  • 使用 store 对象中的属性和方法即可

import counter from './store/Counter';

import { observer } from 'Mobx-react'

function App() {
  const {cart, counter} = useStore()
  return (
    <div className="App">
      <h3>计数器案例</h3>
      <div>点击次数:{counter.count}</div>
      <button onClick={()c=> ounter.increment()}>加1</button>
      <button onClick={()c=> ounter.decrement()}>减1</button>
      <button onClick={() => counter.reset()}>重置</button>
    </div>
  );
}
export default observer(App);

2.5 处理 this 指向问题

  1. 默认 class 中的方法不会绑定 this,this 指向取决于如何调用

<button onClick={() => counter.increment()}>加1</button>

<button onClick={counter.increment}>加1</button>
  1. 在使用 makeObservable 的时候可以通过 action.bound 绑定 this 的指向
  makeObservable(this, {
    count: observable,
    increment: action.bound,
    reset: action.bound,
  })

此时组件中即可直接使用 store 的方法

<button onClick={counter.increment}>加1</button>

2.6 计算属性

  1. computed 可以用来从其他可观察对象中派生信息
  2. 计算值采用惰性求值,会缓存其输出,并且只有当其依赖的可观察对象被改变是才会重新计算
  3. 计算值是一个方法,且方法前面必须使用 get 进行修饰
  4. 计算值需要通过 makeObservable 方法指定
...
  makeObservable(this, {
      count: observable,
      increment: action.bound,
      reset: action.bound,
      double: computed,
    })
...
get double(){
  return this.count * 2
}

2.7 makeAutoObservable 的使用

makeAutoObservable 是加强版的 makeObservable,在默认情况下它将推断所有属性。

推断规格如下:

  1. 所有属性都成为 observable
  2. 所有方法都成为 action
  3. 所有的个体都成为 computed
  
  
  
  makeAutoObservable(this, {decrement: true}, {autoBind: true})

Mobx 监听属性

3.1 autorun 的使用

  1. autorun 函数接受一个函数作为参数,在创建以及每当该函数所观察的值发生变化时,它都应该运行
  2. 当创建 autorun 时,它会运行一次
  3. Mobx 会自动收集并订阅所有可观察属性,一旦有改变发生,autorun 将会再次触发
autorun(() => {
   console.log('counter', counter.count);
})

3.2 reaction 的使用

  1. reaction 类似 autorun,但可以让你更加精细地控制要跟踪的可观察对象
  2. 接受两个函数作为参数
  • 参数 1: data 函数,其返回值将会作为第二个函数输入
  • 参数 2: 回调函数
  1. 与 autorun 不同,reaction 在初始化时不会自动运行
reaction(
  () => counter.count,
  (newValue, oldValue) => {
    console.log('counter.count变化了', newValue, oldValue);
  }
)

Mobx 处理异步

4.1 Mobx 如何处理异步

  1. 异步进程在 Mobx 中不需要任何特殊处理,因为不论是何时引发的所有 reaction 都将会自动更新,
  2. 这是因为可观察对象是可变的,在 action 执行过程中保持对它们的引用一般是安全的
  3. 如果可观察对象的修改不是在 action 函数中,控制台会报警告(可以关闭,但是不推荐)

import { configure } from 'Mobx'
configure({
  
  
  enforceActions: 'never'
})
  
  increment(){
    this.count++
  }
  incrementAsync(){
    setTimeout(this.increment, 1000)
  }

4.2 runInAction 的使用

通过 runInAction 可以保证所有异步更新可观察对象步骤都标识为 action

  
  incrementAsync(){
    setTimeout(() => {
      runInAction(() => {
        this.count++
      })
    }, 1000)
  }

Mobx 模块化

5.1 多个 store 场景

  • 项目规模变大之后,不能将所有状态和方法都放到一个 store 中
  • 我们可以根据业务模块定义多个 store
  • 通过一个根 store 统一管理所有 store

5.2 实现步骤

  • 拆分 Counter 和 Cart 两个 store, 每个 store 都可以有自己的 state/action/computed
  • 在 store/index.ts 文件,导入所有 store,组合成一个 store
  • 使用 useContext 机制,自定义 useStore hook,统一导出 store

import { useContext, createContext } from 'react'
import cart from './Cart'
import counter from './Counter'

class RootStore {
  cart = cart
  counter = counter
}
const store = new RootStore()



const Context = createContext(store)


export const useStore = () => {
  return useContext(Context)
}

import {useStore} from './store'
...
    const {cart, counter} = useStore()
...

各种状态管理熟悉流程后才能更多把握它的一些核心理念,使用起来可能更有心得及感悟。而 Mobx 又更简单化,把大部分东西隐藏起来,如果不去特别研究就不能接触到它的核心/基本思想,会使得我们一直停留在使用层次。研究 Mobx 的源码,多多了解底层思想,这才是关键~

如果您觉得这篇文章有帮助,请点个赞吧~

分享文章

相关文章

更多文章 →
javascript2026-02-24
navigator.sendBeacon全指南
在前端开发中,埋点系统是必不可少的一环。我们经常需要在用户 关闭页面 、 刷新 或 跳转路由 时,向服务器发送最后一条统计数据(比如用户停留时长、页面跳出率)。 但这看似简单的需求,在实现时却危机四伏:请求发不出去?页面跳转卡顿?今天我们就来聊聊这个问题的终极解决方案 —— 。 一、 痛点与传统方案的挣扎 场景还原 当用户点击关闭按钮时,浏览器会触发生命周期事件( 或 )。如果我们直接使用普通的异步 AJAX ( 或 ) 发送请求,浏览...
学习
javascript2025-11-02
理解浏览器事件系统,从用户点击到事件对象的完整旅程
深入理解浏览器事件系统:从用户点击到事件对象的完整旅程 “当我点击页面按钮时,背后发生了什么?为什么回调函数能收到一个包含丰富信息的event对象?今天,让我们一起揭开浏览器事件系统的神秘面纱。” 一个令人困惑的现象 作为前端开发者,我们每天都在写这样的代码: 这段代码如此熟悉,以至于我们很少停下来思考:​ ​这个 对象到底从哪里来?它为什么能知道点击的精确坐标?为什么能识别是哪个元素被点击了?​ ​ 更神奇的是,当我们手动创建事件时:...
学习
javascript2025-10-01
实现大文件上传全流程详解
在日常开发中,大文件上传是个绕不开的坎——动辄几百 MB 甚至 GB 级的文件,直接上传不仅容易超时,还会让用户体验大打折扣。最近我用 Vue+Express 实现了一套完整的大文件上传方案,支持分片上传、断点续传、秒传和手动中。 一、先看效果:我们要实现什么? 先上核心功能清单,确保大家明确目标,知道我们要解决哪些实际问题: 大文件分片上传 :将文件切成固定大小的小片段分批上传,避免单次请求超时 秒传 :服务器已存在完整文件时,直接返...
学习
javascript2025-09-18
JavaScript 的多线程能力:Worker
如果你写过一些计算量稍大的 JavaScript 代码,比如图像处理、大量数据排序或者复杂的算法,你几乎肯定遇到过浏览器“卡死”的现象。点击页面没反应,动画也停了,就像整个世界都静止了。 这就是主线程被阻塞的典型后果。因为主线程既要负责执行 JavaScript,又要负责渲染页面、响应用户操作,一旦它被繁重的计算任务占满,就无暇顾及其他,用户体验便直线下降。 这个问题的根源,正是“主线程是单线程的”。那么,如何解决呢? 答案很简单:把这...
学习面试
javascript2025-09-15
一张 8K 海报差点把首屏拖垮
你给后台管理系统加了一个「企业风采」模块,运营同学一口气上传了 200 张 8K 宣传海报。首屏直接飙到 8.3 s,LCP 红得发紫。 老板一句「能不能像朋友圈那样滑到哪看到哪?」——于是你把懒加载重新翻出来折腾了一轮。 解决方案:三条技术路线,你全踩了一遍 1\. 最偷懒:原生 一行代码就能跑,浏览器帮你搞定。 🔍 关键决策点 2020 年后现代浏览器全覆盖,IE 全军覆没。 必须写死 ,否则 CLS 会抖成 PPT。 适用场景...
学习
javascript2025-09-10
🚀 Web Worker让你的应用丝滑
🌟 引言 在日常的前端开发中,你是否遇到过这样的困扰: 大数据处理时页面卡死 :处理几万条数据时,页面直接卡成PPT,用户点击毫无反应 复杂计算阻塞UI :图片处理、数据分析等计算密集型任务让整个应用假死 文件上传/下载卡顿 :大文件操作时,其他功能完全无法使用 实时数据处理性能差 :WebSocket接收大量数据时,页面渲染严重滞后 今天分享6个Web Worker的核心技巧,让你的应用告别卡顿,用户体验丝滑如德芙! 💡 核心技巧...
学习

评论

请登录后发表评论

去登录
加载评论中...

目录