【学习笔记】从mobx迁移到redux时的概念映射

背景

上一份工作用了很久的mobx,对于它简洁的API、轻量级的特性非常欣赏。新的工作需要转为使用redux,搜了一下redux官方文档,没有看到migrate from mobx的文章,社区也很少有从mobx迁移到redux的介绍,于是自己边学习边记录一下二者的共通之处与不同点,也为后续需要的同学提供参考。

核心概念映射

concepts table

概念 mobx redux
状态 observable state store
触发状态改变 action action, dispatch
状态更新逻辑 - reducer
派生值 computed value selector
副作用 reaction -

mobx 概念流程图

redux 概念流程图

概念详解

observable state vs. store

在mobx中,要想某个变量可以被其他地方自动追踪变化,需要将该变量标记为observable,标记的方法按照变量的类型有类装饰器、属性装饰器、函数几种。读取变量的值非常方便,当做普通值使用即可。详见官方文档中的用法。 在redux中,"可观察变量"的概念是被弱化的,即所有利用redux定义的变量都被写入一个全局的store变量中。如果需要获取store中的某个属性的话,需要使用 store.getState() 来读取。

action vs. dispatch

在mobx中,action的写法是非常自由的,通常为一个接收新值的函数,内部将旧值替换为新值,类似于react的state hook中的setter。对变量改动范围仅限于这个局部属性,接下来受影响的派生值和副作用也仅限于与之相关的局部属性。可以认为它的action是没有boilerplate code的,非常简洁透明,而且影响范围也非常局部。 在redux中,action的写法是非常固定的,格式如下

typescript 复制代码
const addTodoAction = {
  type: 'todos/todoAdded',
  payload: 'Buy milk'
}

个人认为这样写是为了方便reducer消费。相比mobx,boilerplate code比较明显。

[redux独有] reducer

个人认为reducer是redux的核心,它定义了数据更新的所有逻辑,其他的概念如state, action 都是为了reducer服务的。由于要适配action的boilerplate code,一个常见的reducer通常是如下格式

typescript 复制代码
// store
const initialState = { value: 0 }

function counterReducer(state = initialState, action) {
  // action对号入座,这里也常常使用swtich-case语句首先
  if (action.type === 'counter/incremented') {
    return {
    // 复制原始值,这一步必不可少,否则整体state的其他变量将受影响
      ...state,
      // 更新需要更新的值
      value: state.value + 1
    }
  }
  // 非已知的action,返回原始值
  return state
}

computed value vs. selectors

在mobx中,computed value可以视为和vue的计算属性是等同的概念。mobx官网的第一篇文章中有这样一句话,可见它所推崇的是尽可能使用自动计算的派生值。它认为大部分变量应该像Excel表格中的计算公式单元格一样自动更新。有了计算属性,派生值的更新变得非常的清晰和便捷。

Anything that can be derived from the application state, should be. Automatically.

可以使用装饰器或者makeAutoObservable来将变量标注为计算属性,详见官方文档。一个class写法的示例如下

typescript 复制代码
import { makeObservable, observable } from "mobx"

class OrderLine {
    price = 0
    amount = 1
    
    constructor(price) {
    	// 这个函数使得属性自动变成observable, getter自动变成计算属性
        makeAutoObservable(this)
        this.price = price
    }

	// 计算属性
    get total() {
        console.log("Computing...")
        return this.price * this.amount
    }
}

在redux中,selector的直接定义是从store中读取部分state的值,但是我们完全可以把它当做书写计算属性的逻辑的地方,示例如下

typescript 复制代码
// 定义selector
const selectTotalCompletedTodos = state => {
  const completedTodos = state.todos.filter(todo => todo.completed)
  return completedTodos.length
}

// 组件内
const completedTodos = useSelector(selectTotalCompletedTodos)

虽然二者能实现同样的功能,但是在性能上是存在差距的,这里引用DeepSeek的回答

虽然两者都能实现派生状态管理,但 Redux selectors 需要显式优化才能达到 MobX computed 的自动化水平。选择应基于:项目规模、团队习惯、对不可变数据的需求程度。大型复杂应用推荐 Redux+Reselect+RTK Query,快速迭代项目更适合 MobX 的响应式范式。

[mobx独有] reaction

个人认为mobx的reaction可以起到将react的useEffect提到组件外面执行的作用,从而将副作用单独管理,避免受组件渲染周期的影响。具体的写法有autorun reactionwhen 三种,详见官方文档

总结

本文介绍了mobx和redux在概念上共通的部分,并且比较了写法和设计上的差异。当然,这两个库的使用方法和原理以及性能还有很多可以比较的内容,待后续对redux有了更多的实践再进行记录。

相关推荐
__log16 分钟前
ComfyUI 集成技术方案分析报告
javascript·python·django
ZC跨境爬虫24 分钟前
跟着 MDN 学 HTML day_56:(HTML 表格基础完全指南)
前端·javascript·ui·html·音视频
江晓曼*凡云基地37 分钟前
Hermes Agent 多Agent模式:并行拆解复杂任务的实战指南
javascript·windows·microsoft
小白学大数据1 小时前
Python 爬虫动态 JS 渲染与无头浏览器实战选型指南
开发语言·javascript·爬虫·python
飘尘1 小时前
WebAssembly 是什么?它为什么重要?
前端·javascript
之歆1 小时前
DAY_10 JavaScript 深度解析:原型链 · 引用类型 · 内置对象 · 数组方法全攻略(上)
开发语言·javascript·ecmascript
yqcoder3 小时前
Vue 的心脏:深度解析 Vue 2 vs Vue 3 响应式机制
前端·javascript·vue.js
Highcharts.js4 小时前
无需搭建数据管道,如何快速上线投资基金筛选器?
开发语言·javascript·react.js·前端框架·highcharts
kyriewen4 小时前
我让AI替我写Git提交信息,老板以为我每天工作16小时
前端·javascript·git
接着奏乐接着舞4 小时前
react native expo打包
javascript·react native·react.js