【学习笔记】从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有了更多的实践再进行记录。

相关推荐
运维@小兵39 分钟前
vue使用路由技术实现登录成功后跳转到首页
前端·javascript·vue.js
肠胃炎41 分钟前
React构建组件
前端·javascript·react.js
邝邝邝邝丹1 小时前
React学习———React.memo、useMemo和useCallback
javascript·学习·react.js
美酒没故事°1 小时前
纯css实现蜂窝效果
前端·javascript·css
GISer_Jing2 小时前
React useState 的同步/异步行为及设计原理解析
前端·javascript·react.js
mini榴莲炸弹2 小时前
什么是SparkONYarn模式?
前端·javascript·ajax
能来帮帮蒟蒻吗2 小时前
VUE3 -综合实践(Mock+Axios+ElementPlus)
前端·javascript·vue.js·笔记·学习·ajax·typescript
啊啊啊~~2 小时前
歌词滚动效果
javascript·html
球球和皮皮3 小时前
Babylon.js学习之路《四、Babylon.js 中的相机(Camera)与视角控制》
javascript·3d·前端框架·babylon.js
njsgcs4 小时前
opencascade.js stp vite webpack 调试笔记
开发语言·前端·javascript