一、Redux 基础回顾
1.1 Redux 三原则
- 单一数据源:整个应用的 state 存储在一个对象树中
- State 只读:唯一改变 state 的方法是触发 action
- 纯函数修改:使用纯函数 reducer 执行状态更新
1.2 Reducer 基础用法
// 初始状态
const initialState = {
count: 0,
todos: []
}
// 典型 reducer 结构
function appReducer(state = initialState, action) {
switch (action.type) {
case 'INCREMENT':
return {
...state,
count: state.count + 1
};
case 'ADD_TODO':
return {
...state,
todos: [...state.todos, action.payload]
};
default:
return state;
}
}
1.3 核心特性解析
- 纯函数特性:无副作用,相同输入必定得到相同输出
- 不可变性:必须返回新对象,禁止直接修改原 state
- 组合性:通过 combineReducers 组合多个 reducer
二、手写 Redux 核心实现
2.1 最小化 Redux 实现
function createStore(reducer) {
let state;
const listeners = [];
const getState = () => state;
const dispatch = (action) => {
state = reducer(state, action); // 核心逻辑
listeners.forEach(listener => listener());
};
const subscribe = (listener) => {
listeners.push(listener);
return () => {
const index = listeners.indexOf(listener);
listeners.splice(index, 1);
};
};
// 初始化 state
dispatch({ type: '@@INIT' });
return { getState, dispatch, subscribe };
}
2.2 Reducer 处理流程解析
// 模拟 Redux 内部处理逻辑
function dispatch(action) {
// 1. 参数校验
if (!isPlainObject(action)) {
throw new Error('Actions must be plain objects');
}
if (typeof action.type === 'undefined') {
throw new Error('Actions must have a type property');
}
// 2. 执行 reducer 计算新状态
try {
currentState = currentReducer(currentState, action);
} catch (error) {
// 错误处理逻辑
}
// 3. 通知所有订阅者
const listeners = currentListeners;
listeners.forEach(listener => {
listener();
});
}
三、组合 Reducer 实现原理
3.1 combineReducers 源码剖析
function combineReducers(reducers) {
const reducerKeys = Object.keys(reducers);
return function combination(state = {}, action) {
let hasChanged = false;
const nextState = {};
reducerKeys.forEach(key => {
const reducer = reducers[key];
const previousStateForKey = state[key];
const nextStateForKey = reducer(previousStateForKey, action);
nextState[key] = nextStateForKey;
hasChanged = hasChanged || nextStateForKey !== previousStateForKey;
});
return hasChanged ? nextState : state;
};
}
3.2 状态更新优化策略
- 浅层比较:通过
nextStateForKey !== previousStateForKey快速判断状态变化 - 短路优化:只要有一个子 reducer 返回新对象,就触发全局更新
- 引用保留:未变化的子 state 保持原引用
四、性能优化实践
4.1 不可变数据操作对比
| 方式 | 代码示例 | 内存占用 | 性能表现 |
|---|---|---|---|
| 直接修改 | state.items.push(newItem) | 低 | 高 |
| 浅拷贝 | {...state, items: [...items]} | 中 | 中 |
| Immutable.js | state.set('items', newList) | 高 | 高 |
4.2 高效更新模式
// 使用对象解构避免深层拷贝
case 'UPDATE_USER':
return {
...state,
user: {
...state.user,
...action.payload
}
};
// 数组操作优化
case 'DELETE_ITEM':
return {
...state,
items: state.items.filter(
item => item.id !== action.payload
)
};
五、高级模式扩展
5.1 Reducer 组合模式
function undoable(reducer) {
const initialState = {
past: [],
present: reducer(undefined, {}),
future: []
};
return function(state = initialState, action) {
const { past, present, future } = state;
switch (action.type) {
case 'UNDO':
return {
past: past.slice(0, -1),
present: past[past.length - 1],
future: [present, ...future]
};
case 'REDO':
return {
past: [...past, present],
present: future[0],
future: future.slice(1)
};
default:
const newPresent = reducer(present, action);
return {
past: [...past, present],
present: newPresent,
future: []
};
}
};
}
5.2 异步 Action 处理
// 自定义中间件
const asyncMiddleware = store => next => action => {
if (typeof action === 'function') {
return action(store.dispatch, store.getState);
}
return next(action);
};
// 使用示例
const fetchUser = () => async dispatch => {
dispatch({ type: 'USER_REQUEST' });
try {
const user = await api.getUser();
dispatch({ type: 'USER_SUCCESS', payload: user });
} catch (error) {
dispatch({ type: 'USER_FAILURE', error });
}
};
六、最佳实践总结
- 保持 Reducer 纯净:永远不要在 reducer 中执行 API 调用
- 合理拆分粒度:按功能模块划分多个 reducer
- 状态范式化:使用 ID 引用代替嵌套对象
- 性能监控:使用 Redux DevTools 分析更新频率
- 类型安全:配合 TypeScript 实现类型约束
七、思考延伸
- Redux 如何通过时间旅行实现调试功能?
- 为什么 Redux Toolkit 要使用 Immer 处理不可变性?
- 在 10万级数据量下如何优化 Redux 性能?
- Redux 与 Context API 的性能对比分析?
参考资料:
通过本文的学习,建议读者尝试自己实现一个支持中间件系统的增强版 Redux Store,这将帮助您更深刻地理解现代状态管理库的设计哲学。