Redux
Redux 是一个流行的 JavaScript 状态管理库,主要用于管理复杂应用的状态。它由 Dan Abramov 和 Andrew Clark 创建,并受到了 Flux 架构的启发。Redux 的核心理念是维护一个单一的全局状态对象,所有的状态变更都通过一种叫做“action”的方式来描述,然后这些 action 会通过“reducer”函数来更新状态。

MobX 和 Redux 的区别是什么,如何选择?MobX 和 Redux 都是流行的状态管理库,但它们的设计理念和使用方式有很大的不同。选择哪一个取决于项目需求、团队偏好和具体场景。
## 核心设计理念
### MobX
- **基于观察者模式**:自动追踪状态变化,无需手动订阅
- **命令式编程**:直接修改状态,更符合直觉
- **透明响应式**:状态变化自动触发更新
- **灵活性高**:不强制特定的代码结构
### Redux
- **基于函数式编程**:使用纯函数处理状态变化
- **声明式编程**:通过 dispatch action 来修改状态
- **单向数据流**:Action → Reducer → Store → View
- **规范性高**:强制特定的代码结构
## 代码对比
### MobX 示例
```javascript
import { observable, action, computed, makeAutoObservable } from 'mobx';
class TodoStore {
todos = [];
filter = 'all';
constructor() {
makeAutoObservable(this);
}
@computed get filteredTodos() {
switch (this.filter) {
case 'completed':
return this.todos.filter(todo => todo.completed);
case 'active':
return this.todos.filter(todo => !todo.completed);
default:
return this.todos;
}
}
@action addTodo(text) {
this.todos.push({ id: Date.now(), text, completed: false });
}
@action toggleTodo(id) {
const todo = this.todos.find(t => t.id === id);
if (todo) todo.completed = !todo.completed;
}
@action setFilter(filter) {
this.filter = filter;
}
}
const store = new TodoStore();
// 使用
store.addTodo('Learn MobX');
store.toggleTodo(store.todos[0].id);
```
### Redux 示例
```javascript
import { createStore } from 'redux';
// Action Types
const ADD_TODO = 'ADD_TODO';
const TOGGLE_TODO = 'TOGGLE_TODO';
const SET_FILTER = 'SET_FILTER';
// Action Creators
const addTodo = (text) => ({ type: ADD_TODO, payload: { id: Date.now(), text, completed: false } });
const toggleTodo = (id) => ({ type: TOGGLE_TODO, payload: id });
const setFilter = (filter) => ({ type: SET_FILTER, payload: filter });
// Reducer
const initialState = {
todos: [],
filter: 'all'
};
function todoReducer(state = initialState, action) {
switch (action.type) {
case ADD_TODO:
return { ...state, todos: [...state.todos, action.payload] };
case TOGGLE_TODO:
return {
...state,
todos: state.todos.map(todo =>
todo.id === action.payload ? { ...todo, completed: !todo.completed } : todo
)
};
case SET_FILTER:
return { ...state, filter: action.payload };
default:
return state;
}
}
const store = createStore(todoReducer);
// Selector
const selectFilteredTodos = (state) => {
switch (state.filter) {
case 'completed':
return state.todos.filter(todo => todo.completed);
case 'active':
return state.todos.filter(todo => !todo.completed);
default:
return state.todos;
}
};
// 使用
store.dispatch(addTodo('Learn Redux'));
store.dispatch(toggleTodo(store.getState().todos[0].id));
```
## 详细对比
| 特性 | MobX | Redux |
|------|------|-------|
| **编程范式** | 命令式、面向对象 | 函数式、声明式 |
| **状态追踪** | 自动追踪 | 手动订阅 |
| **状态修改** | 直接修改 | 通过 action |
| **代码量** | 较少 | 较多 |
| **学习曲线** | 平缓 | 陡峭 |
| **灵活性** | 高 | 低 |
| **规范性** | 低 | 高 |
| **调试工具** | MobX DevTools | Redux DevTools |
| **时间旅行** | 有限支持 | 完整支持 |
| **中间件** | 不需要 | 丰富(redux-thunk、redux-saga 等) |
| **性能** | 自动优化 | 需要手动优化 |
| **状态结构** | 可以嵌套 | 推荐扁平化 |
| **类型支持** | 良好 | 需要额外配置 |
## 使用场景
### 适合使用 MobX 的场景
1. **快速开发**:需要快速原型开发或小型项目
2. **复杂状态结构**:状态结构复杂且嵌套
3. **团队经验**:团队更熟悉面向对象编程
4. **灵活性优先**:需要更多的代码灵活性
5. **学习成本**:希望降低学习成本
```javascript
// MobX 适合复杂嵌套状态
class UserStore {
@observable user = {
profile: {
name: '',
email: '',
address: {
city: '',
country: ''
}
},
preferences: {
theme: 'light',
language: 'en'
}
};
@action updateCity(city) {
this.user.profile.address.city = city; // 直接修改嵌套属性
}
}
```
### 适合使用 Redux 的场景
1. **大型项目**:需要严格规范的大型项目
2. **团队协作**:多人协作,需要统一的代码规范
3. **时间旅行**:需要完整的时间旅行调试功能
4. **中间件需求**:需要使用丰富的中间件生态
5. **函数式编程**:团队偏好函数式编程范式
```javascript
// Redux 适合扁平化状态
const initialState = {
users: {
byId: {},
allIds: []
},
profiles: {
byId: {},
allIds: []
},
addresses: {
byId: {},
allIds: []
}
};
// 通过 reducer 处理状态变化
function reducer(state = initialState, action) {
switch (action.type) {
case UPDATE_CITY:
return {
...state,
addresses: {
...state.addresses,
byId: {
...state.addresses.byId,
[action.payload.id]: {
...state.addresses.byId[action.payload.id],
city: action.payload.city
}
}
}
};
default:
return state;
}
}
```
## 性能对比
### MobX 性能优势
1. **自动优化**:自动追踪依赖,只更新必要的组件
2. **批量更新**:action 内部的状态变化会被批量处理
3. **懒计算**:computed 值只在被访问时才计算
```javascript
// MobX 自动优化
class Store {
@observable items = [];
@computed get expensiveValue() {
console.log('Computing expensive value');
return this.items.reduce((sum, item) => sum + item.value, 0);
}
}
// 只有在访问 expensiveValue 时才会计算
console.log(store.expensiveValue); // 计算一次
console.log(store.expensiveValue); // 使用缓存,不计算
```
### Redux 性能挑战
1. **手动优化**:需要使用 reselect、memo 等工具优化
2. **全量比较**:每次 dispatch 都会比较整个状态树
3. **需要手动订阅**:需要手动选择需要的数据
```javascript
// Redux 需要手动优化
import { createSelector } from 'reselect';
const selectItems = (state) => state.items;
const selectExpensiveValue = createSelector(
[selectItems],
(items) => {
console.log('Computing expensive value');
return items.reduce((sum, item) => sum + item.value, 0);
}
);
// 需要手动选择数据
const value = selectExpensiveValue(store.getState());
```
## 调试对比
### MobX 调试
```javascript
// 使用 MobX DevTools
import { makeObservable, observable, action } from 'mobx';
class Store {
@observable count = 0;
constructor() {
makeObservable(this);
}
@action increment() {
this.count++;
}
}
// 在浏览器中查看状态变化
```
### Redux 调试
```javascript
// 使用 Redux DevTools
import { createStore } from 'redux';
const store = createStore(
reducer,
window.__REDUX_DEVTOOLS_EXTENSION__ && window.__REDUX_DEVTOOLS_EXTENSION__()
);
// 可以查看完整的 action 历史、状态变化和时间旅行
```
## 迁移建议
### 从 MobX 迁移到 Redux
1. **重构状态结构**:将嵌套状态扁平化
2. **创建 action types**:定义所有可能的 action
3. **编写 reducers**:将状态修改逻辑移到 reducers
4. **使用中间件**:根据需要添加中间件
5. **更新组件**:使用 useSelector 和 useDispatch
### 从 Redux 迁移到 MobX
1. **创建 stores**:将 reducer 逻辑转换为 stores
2. **使用 observable**:将状态转换为 observable
3. **添加 actions**:将 action creators 转换为 actions
4. **更新组件**:使用 observer 或 useObserver
5. **简化代码**:移除不必要的样板代码
## 总结
**选择 MobX 如果:**
- 需要快速开发
- 状态结构复杂且嵌套
- 团队更熟悉面向对象编程
- 希望降低学习成本
- 需要更多的代码灵活性
**选择 Redux 如果:**
- 项目规模较大
- 需要严格的代码规范
- 需要完整的时间旅行调试
- 需要丰富的中间件生态
- 团队偏好函数式编程
两者都是优秀的状态管理库,选择哪一个应该基于项目需求和团队情况。在实际项目中,也可以根据不同模块的特点混合使用。
前端 · 2月22日 14:08
MobX 和 Redux 有什么区别,应该如何选择?MobX 和 Redux 是两种流行的状态管理库,它们在设计理念和使用方式上有显著差异:
## 架构设计
**Redux**:
- 采用单向数据流架构
- 遵循严格的不可变性原则
- 使用纯函数(reducers)来处理状态更新
- 状态是只读的,只能通过 dispatch action 来修改
- 需要手动选择需要的状态(通过 useSelector)
**MobX**:
- 采用响应式编程架构
- 允许可变状态,但通过 observable 进行追踪
- 可以直接修改状态(在 action 中)
- 自动追踪依赖关系,自动更新相关组件
- 无需手动选择状态,组件自动订阅所需数据
## 代码量和复杂度
**Redux**:
- 需要编写大量的样板代码(actions、action creators、reducers)
- 需要配置 store、middleware、reducers
- 代码结构相对复杂,学习曲线陡峭
**MobX**:
- 代码量少,简洁直观
- 最小化配置,开箱即用
- 学习曲线平缓,容易上手
## 性能
**Redux**:
- 通过 shallowEqual 进行浅比较来决定是否重新渲染
- 需要开发者手动优化性能(如使用 reselect)
- 对于大型应用,可能需要额外的优化策略
**MobX**:
- 细粒度的依赖追踪,只更新真正需要更新的组件
- 自动缓存计算属性,避免不必要的计算
- 性能优化是自动的,开发者无需过多关注
## TypeScript 支持
**Redux**:
- 需要为 actions、reducers、state 等定义类型
- 类型定义相对复杂,但类型安全性高
- 需要使用类型断言或类型守卫
**MobX**:
- 类型推断更自然,类型定义更简单
- 与 TypeScript 集成更流畅
- 可以充分利用 TypeScript 的类型推断能力
## 调试和可预测性
**Redux**:
- 状态变化完全可预测,易于调试
- Redux DevTools 提供强大的时间旅行调试功能
- 所有的状态变化都通过 action 记录
**MobX**:
- 调试相对复杂,因为状态可以在多处修改
- MobX DevTools 提供了调试支持,但不如 Redux 强大
- 需要遵循最佳实践(如使用 action)来提高可预测性
## 适用场景
**选择 Redux**:
- 需要严格的状态管理规范
- 团队规模大,需要明确的代码结构
- 需要时间旅行调试
- 状态变化逻辑复杂,需要中间件支持
**选择 MobX**:
- 追求开发效率和代码简洁性
- 项目规模中小型
- 需要快速原型开发
- 团队对函数式响应式编程更熟悉
## 总结
Redux 更适合需要严格架构和可预测性的大型项目,而 MobX 更适合追求开发效率和简洁性的项目。选择哪种库应该根据项目需求、团队经验和长期维护考虑来决定。
前端 · 2月21日 15:50
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处理。
这只是自定义中间件的一个简单例子,但您可以根据需要在中间件中实现更复杂的逻辑,例如异步操作、路由导航或其他您想要的任何自定义行为。
前端 · 2024年8月5日 12:48