前端5月31日 15:55
MobX 和 Redux 到底该怎么选?适合哪些场景?MobX 和 Redux 的区别不只是 API 写法不同,而是状态管理哲学不同。Redux 强调显式数据流:组件 dispatch action,reducer 生成新 state,状态变化可以被记录和回放。MobX 强调响应式模型:你修改 observable,系统自动知道哪些 computed、reaction 或 observer 组件需要更新。
如果用一句话选型:需要强约束、审计和统一协作时偏 Redux;需要快速建模复杂业务对象、减少样板代码时偏 MobX。现在 Redux Toolkit 已经大幅减少模板代码,所以不能再简单说“Redux 一定啰嗦”。但 MobX 在深层对象、表单状态和局部复杂交互里仍然很顺手。
```ts
// Redux Toolkit
const slice = createSlice({
name: "counter",
initialState: { value: 0 },
reducers: {
inc: state => { state.value += 1; }
}
});
// MobX
class CounterStore {
value = 0;
constructor() { makeAutoObservable(this); }
inc() { this.value += 1; }
}
```
Redux Toolkit 里看起来也能“直接改 state”,但那是 Immer 帮你生成不可变结果。MobX 的直接修改则是它本身的响应式模型,依赖追踪发生在读取和写入之间。两者都能写得很现代,真正影响选择的是团队调试方式、业务复杂度和长期维护成本。
还有一个现实因素是招聘和交接成本。Redux 的资料、范式和候选人经验更多,新人即使没接触过项目,也容易顺着 action、slice、selector 找到入口。MobX 项目如果 store 设计得好,上手同样很快;如果设计得随意,新人需要先理解一套隐式依赖网络。选型时把团队未来一年的人数变化也算进去,往往比单纯比较代码量更实际。
## 追问
### Redux 的优势现在还明显吗?
明显,尤其是在多人协作和复杂状态审计场景里。Redux 的 action 日志、DevTools、时间旅行调试仍然很强,线上问题复盘时能看到状态如何一步步变化。取舍是你要接受更明确的流程和更多约束,哪怕 Redux Toolkit 已经减少了不少样板。金融、交易、权限流转这类系统,显式数据流带来的可追溯性通常比少写几行代码更重要。
### MobX 更适合哪些业务?
MobX 适合状态结构像业务对象一样自然变化的场景,比如复杂表单、编辑器、看板、低代码配置器和局部交互很多的后台页面。它允许你用 class 表达领域模型,用 computed 表达派生值,用 observer 自动连接 UI。边界是自由度越高,团队规范越重要。若大家随手在任意位置改 observable,又不给 action 命名,后期排查会比 Redux 更痛苦。
### 性能上 MobX 一定比 Redux 更好吗?
不一定,但 MobX 的默认更新粒度通常更细。它追踪组件实际读取的 observable 字段,所以某个字段变化只影响真正用到它的组件。Redux 依赖 selector 和引用比较,写得好同样很快,写得差则容易因为新对象、新数组导致重复渲染。取舍在于 MobX 把优化自动化,Redux 把优化显式化;前者省心,后者更可控。
### TypeScript 项目选哪一个更舒服?
MobX 的 class 模型和 TypeScript 搭配很自然,字段、方法、getter 的类型就是业务模型本身。Redux Toolkit 的类型体验也已经比旧 Redux 好很多,`createSlice` 能推断 action 和 state,但异步 thunk、RootState、Dispatch 仍然需要一些模板。取舍是 MobX 写业务模型更顺,Redux 写团队规范更统一。大型团队里,类型舒服不一定是唯一目标,统一的数据流和工具链也很值钱。
### 能不能在一个项目里同时用 MobX 和 Redux?
可以,但要非常克制。比如全局登录态、权限、审计相关状态放 Redux,某个复杂编辑器内部用 MobX 管局部模型,这是有边界的混用。踩坑点是没有划清职责,导致同一份数据在两个 store 里各存一份,最终同步逻辑比状态管理本身还复杂。除非收益明确,否则更建议选一个主方案,再用局部 React state 或轻量库补足边角。
如果项目并不复杂,却又觉得 Redux 和 MobX 都偏重,也可以把 Zustand、Jotai、Valtio 这类轻量方案纳入比较。Zustand API 简单,适合轻量全局状态;Jotai 更偏原子化组合;Valtio 则接近可变对象代理的体验。这里的取舍是生态、团队熟悉度和调试能力,不要只看示例代码短不短。状态管理选型最怕为了“新”而换,最后业务复杂度没降,团队学习成本反而升了。
落地时可以先画出状态的生命周期:哪些状态跨页面共享,哪些只服务某个复杂组件,哪些需要被审计或回放。跨团队、跨流程的状态更适合 Redux 这种强约束方案;局部领域模型、频繁编辑和深层对象更适合 MobX。这个判断比“哪个库更流行”靠谱,因为状态管理的问题通常不是 API 不够漂亮,而是边界没有定义清楚。
所以 MobX 和 Redux 没有绝对胜负。Redux 像一套清晰的交通规则,MobX 像更灵活的自动导航;项目越重协作和审计,越需要规则,项目越重局部复杂交互,越能体现 MobX 的效率。标签
Redux
Redux 是一个流行的 JavaScript 状态管理库,主要用于管理复杂应用的状态。它由 Dan Abramov 和 Andrew Clark 创建,并受到了 Flux 架构的启发。Redux 的核心理念是维护一个单一的全局状态对象,所有的状态变更都通过一种叫做“action”的方式来描述,然后这些 action 会通过“reducer”函数来更新状态。

服务端5月28日 01:18
Zustand 与 Redux 相比有哪些优缺点?### Zustand 的核心优势
**极简 API,告别样板代码**
Zustand 创建 store 只需一个函数调用,无需定义 action types、reducers、action creators。与 Redux Toolkit 的 createSlice 相比,代码量减少 60% 以上:
```js
// Zustand — 一个函数搞定 store
import { create } from 'zustand'
const useStore = create((set) => ({
count: 0,
increment: () => set((state) => ({ count: state.count + 1 })),
decrement: () => set((state) => ({ count: state.count - 1 })),
}))
// 组件中直接用 hook
function Counter() {
const { count, increment } = useStore()
return <button onClick={increment}>{count}</button>
}
```
```js
// Redux Toolkit — 需要更多概念
import { createSlice, configureStore } from '@reduxjs/toolkit'
import { Provider, useSelector, useDispatch } from 'react-redux'
const counterSlice = createSlice({
name: 'counter',
initialState: { value: 0 },
reducers: {
increment: (state) => { state.value += 1 },
decrement: (state) => { state.value -= 1 },
},
})
const store = configureStore({ reducer: { counter: counterSlice.reducer } })
// 还需要 Provider 包裹 + useSelector + useDispatch
function Counter() {
const count = useSelector((s) => s.counter.value)
const dispatch = useDispatch()
return <button onClick={() => dispatch(increment())}>{count}</button>
}
```
**无需 Provider,更干净的组件树**
Redux 必须在应用顶层包裹 `<Provider store={store}>`,导致组件树多出一层嵌套,测试时也需要用 `<Provider>` 包裹。Zustand 直接在模块作用域创建 store,组件通过 hook 消费状态,无需任何包裹组件。这对存量项目集成尤其友好——不用改已有的组件树结构就能接入状态管理。
**更小的体积,更快的加载**
| 库 | Gzipped 大小 |
|---|---|
| Zustand | ~1.1 KB |
| Redux Toolkit | ~8.2 KB |
| Redux Toolkit + React-Redux | ~11.8 KB |
在 3G 网络下,11 KB 的差距可带来 50-100ms 的交互时间优化。对包体积有严格限制的移动端 H5 场景,Zustand 优势明显。
**内置选择器,精准控制重渲染**
Zustand 支持细粒度订阅,只订阅需要的状态切片,自动跳过无关更新:
```js
// 只订阅 count,其他状态变化不会触发重渲染
const count = useStore((state) => state.count)
// 也支持 shallow 比较对象
import { shallow } from 'zustand/shallow'
const { count, name } = useStore(
(state) => ({ count: state.count, name: state.name }),
shallow
)
```
Redux 的 `useSelector` 同样支持选择器,但默认使用 `===` 严格比较,返回对象时需要手动使用 `shallowEqual`,容易遗忘导致不必要的重渲染。
**TypeScript 开箱即用**
Zustand 的 store 定义自带类型推导,无需额外声明类型:
```ts
const useStore = create<{ count: number; increment: () => void }>((set) => ({
count: 0,
increment: () => set((s) => ({ count: s.count + 1 })),
}))
// count 和 increment 类型自动推导,无需泛型
```
Redux Toolkit 虽然也有良好的 TS 支持,但 createSlice 和 configureStore 需要更多类型标注和泛型配置。
**异步操作更直观**
Zustand 处理异步不需要额外中间件,直接写 async 函数:
```js
const useStore = create((set) => ({
data: null,
loading: false,
fetchData: async () => {
set({ loading: true })
const res = await fetch('/api/data')
const data = await res.json()
set({ data, loading: false })
},
}))
```
Redux Toolkit 通过 createAsyncThunk 处理异步,需要定义 pending/fulfilled/rejected 三种状态,样板代码更多。
### Zustand 的不足
**生态和中间件相对薄弱**
Redux 拥有 Redux Saga、Redux Observable、Redux Persist 等成熟中间件生态,处理竞态、取消、重试等复杂副作用有成熟方案。Zustand 自带 persist、devtools、immer 等中间件,覆盖常见需求,但社区中间件数量和成熟度仍远不及 Redux。遇到复杂副作用场景时,往往需要自己实现或组合多个中间件。
**调试体验有差距**
Zustand 可通过 `devtools` 中间件接入 Redux DevTools,但时间旅行调试和 action 回放功能不如原生 Redux 完善。对于需要严格追踪每次状态变更、回溯 bug 的场景,Redux 的调试体验更可靠。
**大型项目实践尚在积累**
Zustand 已被 React Three Fiber、shadcn/ui、Next.js 示例等项目广泛采用,2026 年 npm 周下载量达 700 万次,但在超大型企业级应用中的最佳实践仍不如 Redux 成熟。缺少千人团队的治理模式参考和大规模重构案例。
**灵活性的双刃剑**
Zustand 不强制状态更新模式,不同开发者可能写出风格迥异的 store。在缺乏 Code Review 约束的团队中,灵活反而变成混乱。Redux 的严格单向数据流天然约束了风格统一性,新人接手代码时理解成本更低。
### 什么时候选 Zustand
- **中小型项目**:状态逻辑不复杂,追求开发速度和简洁性
- **存量项目集成**:不想引入 Provider 改动组件树,零侵入接入
- **性能敏感场景**:对包体积和重渲染有严格要求,如移动端 H5
- **快速原型开发**:重视迭代速度而非架构完备性
- **React Three Fiber / 可视化项目**:Zustand 是 R3F 生态的默认选择
### 什么时候选 Redux
- **大型企业级应用**:团队 5 人以上,需要标准化流程和严格约束
- **复杂副作用**:需要 Saga 级别的竞态处理、取消和重试能力
- **重度调试需求**:时间旅行调试对排查线上问题至关重要
- **团队已熟悉 Redux**:迁移成本高于收益,不必为了换而换
- **金融 / 交易类系统**:需要追踪每一次状态变更的审计场景
### 怎么选:一句结论
项目小、求快、求轻选 Zustand;项目大、求稳、求规范选 Redux。两者并非互斥——复杂项目中可以在全局状态用 Redux、局部状态用 Zustand,按需搭配。前端5月27日 23:31
MobX 和 Redux 有什么区别?MobX 和 Redux 有什么区别?面试中三句话说清楚:MobX 是响应式自动追踪,改了数据视图自动更新;Redux 是函数式单向数据流,必须 dispatch action 才能改状态。MobX 写得少但调试难预测,Redux 写得多但状态可追溯。选哪个看团队——要快用 MobX,要严用 Redux。
## 核心区别
| 维度 | MobX | Redux |
|------|------|-------|
| 编程范式 | 响应式 + 面向对象 | 函数式 + 单向数据流 |
| 状态修改 | 直接赋值,自动追踪 | dispatch action → reducer 返回新状态 |
| 样板代码 | 极少 | 较多(即使 RTK 也比 MobX 多) |
| 状态结构 | 嵌套对象随意写 | 推荐扁平化 + normalize |
| 时间旅行 | 有限支持 | Redux DevTools 完整支持 |
| 学习曲线 | 入门快,精通需理解响应式原理 | 入门慢,但模式固定好掌握 |
| TypeScript | 良好 | 良好(RTK 出厂即支持) |
## 代码对比:同一个 Todo
### MobX 写法
```javascript
import { makeAutoObservable, computed } from "mobx";
class TodoStore {
todos = [];
filter = "all";
constructor() {
makeAutoObservable(this);
}
get filteredTodos() {
if (this.filter === "completed") return this.todos.filter((t) => t.done);
if (this.filter === "active") return this.todos.filter((t) => !t.done);
return this.todos;
}
addTodo(text) {
this.todos.push({ id: Date.now(), text, done: false });
}
toggle(id) {
const todo = this.todos.find((t) => t.id === id);
if (todo) todo.done = !todo.done;
}
}
```
直接改属性,MobX 内部的依赖追踪机制会自动触发对应组件重渲染。这就是响应式的核心——你写的是普通赋值,背后 MobX 帮你做了订阅和通知。
### Redux Toolkit 写法
2026 年 Redux 官方推荐用 Redux Toolkit(RTK),不再用 `createStore` 那套手写模板。
```javascript
import { createSlice, configureStore, createSelector } from "@reduxjs/toolkit";
const todoSlice = createSlice({
name: "todos",
initialState: { items: [], filter: "all" },
reducers: {
addTodo: (state, action) => {
state.items.push({ id: Date.now(), text: action.payload, done: false });
},
toggle: (state, action) => {
const todo = state.items.find((t) => t.id === action.payload);
if (todo) todo.done = !todo.done;
},
setFilter: (state, action) => {
state.filter = action.payload;
},
},
});
export const { addTodo, toggle, setFilter } = todoSlice.actions;
const store = configureStore({ reducer: { todos: todoSlice.reducer } });
// Selector(带 memo)
const selectFiltered = createSelector(
[(s) => s.todos.items, (s) => s.todos.filter],
(items, filter) => {
if (filter === "completed") return items.filter((t) => t.done);
if (filter === "active") return items.filter((t) => !t.done);
return items;
}
);
```
RTK 内置了 Immer,所以在 reducer 里可以直接修改state(实际产出的是不可变新对象)。这大大减少了 Redux 的样板代码量。
## 面试追问:MobX 的响应式原理是什么?
MobX 在属性读取时收集依赖(通过 Proxy 或 getter 劫持),在属性写入时通知所有观察者。组件渲染时读取 observable 属性,MobX 记录这个组件依赖这些属性;属性变化时,MobX 精确触发对应组件重渲染。所以 MobX 不需要手动 `shouldComponentUpdate` 或 `React.memo`,它天然做到了最小化更新。代价是调试时不容易追踪谁改了这个值,因为赋值点分散在代码各处。
## 面试追问:为什么 Redux 要求状态不可变?
两个原因。第一,不可变让引用比较成为可能——`oldState !== newState` 就知道状态变了,不用深比较,这是 Redux 性能模型的基础。第二,不可变保证了时间旅行调试——每次状态变更都产生新的快照,可以回退到任意历史节点。如果直接修改原对象,历史状态会被覆盖,DevTools 的时间旅行就废了。这也是 MobX 时间旅行支持有限的根本原因。
## 性能:谁更快?
2026 年基准测试数据:
| 操作 | MobX | Redux Toolkit |
|------|------|---------------|
| 简单更新 | 0.3ms | 0.8ms |
| 嵌套更新 | 0.4ms | 1.2ms |
| 内存占用 | 3.1MB | 4.2MB |
MobX 快在哪?它自动追踪依赖,只更新真正受影响的组件。Redux 每次 dispatch 后要过一遍 `useSelector` 的比较逻辑,组件需要自己决定要不要重渲染。当然,Redux 配合 `reselect` 做 memo 化后差距会缩小,但这是需要开发者手动做的。
## 怎么选?
**选 MobX:** 小团队快速迭代、状态嵌套深(比如树形编辑器)、团队 OOP 背景强、不想写样板代码。
**选 Redux (RTK):** 大型项目多人协作、需要严格的代码规范和可追溯的状态变更、需要 DevTools 时间旅行、团队函数式偏好。
**都不选?** 2026 年 Zustand(2.1KB)因为极简 API 和零样板代码,成为很多新项目的默认选择。它没有 MobX 的响应式黑盒,也没有 Redux 的模板负担。如果你的项目状态管理不复杂,Zustand 值得一看。
## 一句话总结
MobX 用魔法帮你省事,Redux 用规矩帮你兜底。面试答区别,先说范式(响应式 vs 函数式),再说可变性(可变 vs 不可变),最后说取舍(灵活 vs 可预测)。前端2024年8月5日 12:48
Redux 如何实现自定义中间件在Redux中,中间件是一种强大的机制,允许开发者在action被发送到reducer之前插入自己的逻辑。创建自定义的Redux中间件涉及到编写一个函数,该函数按照Redux中间件API的规格返回一个满足特定签名的函数。
我将向您展示如何自定义实现一个简单的日志中间件,该中间件的作用是在action被派发时在控制台输出日志信息。
以下是自定义Redux中间件的基本步骤:
1. 编写一个函数,该函数接收`store`的`dispatch`和`getState`方法。
2. 该函数返回一个接收下一个中间件的`next`函数的函数。
3. 返回的函数再返回一个接收action的函数。
4. 在最内层的函数体内,可以执行自定义的逻辑,然后调用`next(action)`将action传递给链中的下一个中间件或reducer。
下面是一个自定义日志中间件的例子:
```javascript
// 自定义日志中间件
const loggerMiddleware = store => next => action => {
// 自定义的逻辑:在当前action被处理之前输出日志
console.log('dispatching', action);
// 调用链中的下一个中间件或reducer
let result = next(action);
// 自定义的逻辑:在action被处理后输出新的状态
console.log('next state', store.getState());
// 返回result,因为middleware的链需要从next(action)获取返回值
return result;
};
export default loggerMiddleware;
```
在上述的中间件代码中:
- `store`: Redux store实例,它包含了`dispatch`和`getState`方法。
- `next`: 是一个将action传递给链中下一个处理者(中间件或reducer)的函数。
- `action`: 是当前正在处理的action对象。
使用这个中间件的典型方式是在创建Redux store时应用它:
```javascript
import { createStore, applyMiddleware } from 'redux';
import rootReducer from './reducers';
import loggerMiddleware from './middleware/loggerMiddleware';
// 使用applyMiddleware来增强store,添加自定义的loggerMiddleware
const store = createStore(
rootReducer,
applyMiddleware(loggerMiddleware)
);
export default store;
```
在这个例子中,任何派发到store的action都会先经过`loggerMiddleware`这个中间件,在控制台输出action信息,然后继续沿中间件链传递,直到最终被reducer处理。
这只是自定义中间件的一个简单例子,但您可以根据需要在中间件中实现更复杂的逻辑,例如异步操作、路由导航或其他您想要的任何自定义行为。