跨端状态管理方案深度解析
跨端状态管理:Redux、MobX 等方案的原理与响应式思想,及在 RN/Flutter 的映射。 讲清选型要点,掌握后能答出「跨端状态管理怎么统一」。
一句话概括
Redux 和 MobX 的本质区别不是”函数式 vs 面向对象”,而是数据流是否显式可追踪——Redux 让你看到每一步变化,MobX 让你感觉不到在管理状态。
核心知识点
1. Redux:显式单向数据流
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
// Redux 的核心:dispatch → reducer → new state → UI render
// 每一步都是显式的、可追溯的、可记录日志的
// 三个概念记牢:
// Store = 整个 App 的一棵状态树(单一数据源)
// Action = { type, payload },描述"发生了什么"
// Reducer = (state, action) => newState,纯函数,不能有副作用
// Redux Toolkit 让你不用手写 action type 常量和 switch-case:
import { createSlice } from '@reduxjs/toolkit';
const cartSlice = createSlice({
name: 'cart',
initialState: { items: [], total: 0 },
reducers: {
addItem(state, action) {
state.items.push(action.payload); // Immer 让你"可变"写法
state.total += action.payload.price; // 但底层仍是不可变更新
},
clearCart(state) { state.items = []; state.total = 0; },
},
});
2. MobX:隐式响应式追踪
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
// MobX 的核心:observable → computed → reaction
// 你只管改数据,框架自动追踪谁依赖了谁
import { makeAutoObservable } from 'mobx';
class CartStore {
items = [];
constructor() { makeAutoObservable(this); }
get total() { return this.items.reduce((s, i) => s + i.price, 0); } // computed
get isEmpty() { return this.items.length === 0; }
addItem(item) { this.items.push(item); } // action,自动批处理
}
// 组件中用 observer 包裹,items 变了自动 re-render
// 你不需要 dispatch,不需要 selector,MobX 自己算依赖图
3. 中间件洋葱模型
1
dispatch(action) → mid1(before) → mid2(before) → reducer → mid2(after) → mid1(after)
1
2
3
4
5
6
7
8
9
10
11
12
// Redux 中间件本质是一个函数:
const loggerMiddleware = store => next => action => {
console.log('dispatching', action);
const result = next(action); // 交给下一个中间件或 reducer
console.log('next state', store.getState());
return result;
};
// 跨端价值:不同平台注入不同中间件
// iOS/Android → NativeLogger 中间件,调用原生日志
// 小程序 → 自动 wx.setStorageSync 中间件
// Web → Redux DevTools 中间件
4. Immutable 结构共享
1
2
3
4
5
6
7
8
9
// Redux 要求返回全新对象,但全量深拷贝太贵
// 解法:结构共享 —— 没变的部分直接复用引用
const oldState = { user: { name: 'Tom' }, count: 1 };
const newState = { user: oldState.user, count: 2 }; // user 引用没变!
// Redux 的 useSelector 用 === 比较,user 引用相同就跳过重渲染
// RTK 内置 Immer,你写"可变"代码,它生成不可变结果
// 这就是为什么 createSlice 的 reducer 里能写 state.value += 1
5. Selector 缓存机制
1
2
3
4
5
6
7
8
9
10
11
12
13
// createSelector 只在依赖变化时重新计算
import { createSelector } from '@reduxjs/toolkit';
const selectItems = state => state.cart.items;
const selectFilter = state => state.cart.filter;
const selectFilteredItems = createSelector(
[selectItems, selectFilter],
(items, filter) => { // items 或 filter 没变就返回缓存值,不重新算
console.log('重新计算'); // 只在必要时打印
return items.filter(i => i.category === filter);
}
);
其实你每天都在用
- Redux 的 reducer:跟
Array.reduce((acc, cur) => ...)完全一样的签名,(累积状态, 当前操作) => 下一个状态 - MobX 的 makeAutoObservable:相当于 Vue 3 的
reactive(),你改了对象属性,框架自动追踪 - useSelector:类似 React 的
useContext,但多了一层 === 比较来判断要不要 rerender - Redux DevTools:能回放、快进、跳过任意 action——这不是魔法,是 Redux 把每一步 state 都存了下来
- action 批处理:
runInAction(() => { a=1; b=2; })内部改 100 次也只触发一次 re-render,跟 Vue 的nextTick批量更新思路一致
常见误解(FAQ)
❌ 误区一:”Redux 已经不流行了,应该用 Zustand/Jotai”
Redux Toolkit 和传统手写 Redux 是完全不同的体验。RTK 内置 Immer、createAsyncThunk、RTK Query,样板代码量已经接近 Zustand。Redux 的独特优势在于中间件生态和 DevTools 时间旅行调试——这两个在大团队项目中仍然是杀手级特性。
❌ 误区二:”MobX 不适合大型项目”
MobX 在大型项目中的真正挑战不是性能,而是可预测性。当 20 个 observer 组件订阅了同一个 observable 对象的不同属性,一次修改到底会触发几个组件更新?这需要你理解整个依赖图。Redux 则相反——一条 dispatch 链路清清楚楚。所以”适合大项目”指的不是规模,而是你是否需要显式的可追溯性。
❌ 误区三:”MobX 不用 selector,性能会差”
MobX 的依赖粒度比 selector 更细——selector 通常是”过滤整个列表”,MobX 可以精确到列表第 3 项的 name 属性变了才触发对应组件。只是在深层嵌套场景下,这个依赖图的维护本身有开销。Redux 的 selector 在有 createSelector 缓存时通常更可预测。
一句话总结
选 Redux 还是 MobX 的终极问题不是技术偏好,而是一年后你还能不能看懂自己的状态流转——Redux 让你反复读日志复盘,MobX 让你写得飞快但事后难追溯。