Redux 是一个用于 JavaScript 应用的状态管理库,其核心思想是通过单一数据源(Store) 、不可变状态 和纯函数(Reducer)来管理应用状态。以下是 Redux 的核心原理及一个简化实现:
一、Redux 核心原理
-
Store
- 全局唯一的状态容器,通过
createStore
创建。 - 提供
getState()
获取当前状态、dispatch(action)
触发状态更新、subscribe(listener)
订阅状态变化。
- 全局唯一的状态容器,通过
-
Action
- 普通的 JavaScript 对象,必须包含
type
字段,描述要执行的操作(如{ type: 'INCREMENT' }
)。
- 普通的 JavaScript 对象,必须包含
-
Reducer
- 纯函数,接收当前状态和 Action,返回新的状态(
state = reducer(state, action)
)。 - 必须是不可变的(每次返回新对象,而非直接修改原状态)。
- 纯函数,接收当前状态和 Action,返回新的状态(
-
数据流
- 单向数据流 :
View → Dispatch(Action) → Reducer → 新State → View 更新
。
- 单向数据流 :
二、简化版 Redux 实现
以下是一个极简版的 Redux 实现(省略中间件、异步等高级功能):
javascript
// 1. createStore: 创建 Store
function createStore(reducer) {
let state; // 当前状态
let listeners = []; // 订阅者列表
// 获取当前状态
const getState = () => state;
// 分发动作:调用 Reducer 更新状态,并通知订阅者
const dispatch = (action) => {
state = reducer(state, action); // 计算新状态
listeners.forEach(listener => listener()); // 通知所有订阅者
return action; // 返回 Action(可选)
};
// 订阅状态变化
const subscribe = (listener) => {
listeners.push(listener);
// 返回取消订阅的函数
return () => {
listeners = listeners.filter(l => l !== listener);
};
};
// 初始化 State:首次调用 Reducer,传入 undefined 和空 Action
dispatch({ type: '@@INIT' }); // 自定义初始化 Action
return { getState, dispatch, subscribe };
}
// 2. Reducer 示例:计数器
function counterReducer(state = { count: 0 }, action) {
switch (action.type) {
case 'INCREMENT':
return { ...state, count: state.count + 1 }; // 不可变更新
case 'DECREMENT':
return { ...state, count: state.count - 1 };
default:
return state; // 未知 Action 返回原状态
}
}
// 3. 使用示例
const store = createStore(counterReducer);
// 订阅状态变化
const unsubscribe = store.subscribe(() => {
console.log('State changed:', store.getState());
});
// 触发 Action
store.dispatch({ type: 'INCREMENT' }); // State: { count: 1 }
store.dispatch({ type: 'INCREMENT' }); // State: { count: 2 }
store.dispatch({ type: 'DECREMENT' }); // State: { count: 1 }
// 取消订阅
unsubscribe();
三、关键点解析
-
初始化 State
- 首次调用
dispatch({ type: '@@INIT' })
,触发 Reducer 返回初始状态。
- 首次调用
-
不可变更新
- Reducer 必须返回新对象(如
{ ...state, count: state.count + 1 }
),而不是直接修改原状态。
- Reducer 必须返回新对象(如
-
订阅机制
subscribe
将监听函数存入数组,dispatch
后遍历调用所有监听函数。
-
简化假设
- 省略了中间件(如
redux-thunk
)、异步 Action、CombineReducers 等功能。
- 省略了中间件(如
Redux 中间件(Middleware)的核心作用是在 dispatch
发起 Action 到 Reducer 处理 Action 的过程中,插入自定义逻辑 (如异步操作、日志记录、错误处理等)。它的本质是一个函数增强器 ,通过层层嵌套包裹原始的 dispatch
方法,扩展其功能。
四、Redux 中间件原理
1. 中间件的核心目标
- 在
dispatch(action)
执行前后插入自定义逻辑(如异步请求、日志打印)。 - 不修改 Redux 核心代码,通过组合的方式扩展
dispatch
。
2. 中间件的执行流程
- 用户调用
store.dispatch(action)
。 - 中间件拦截
dispatch
,执行自定义逻辑(如发起异步请求)。 - 中间件决定是否继续调用下一个中间件或最终的
dispatch
。 - 最终调用原始的
dispatch
触发 Reducer 更新状态。
3. 中间件的签名
中间件是一个三参数函数 ,接收 store
的 dispatch
和 getState
方法,返回一个新的 dispatch
函数:
javascript
const middleware = ({ dispatch, getState }) => next => action => {
// 自定义逻辑(如异步、日志)
return next(action); // 调用下一个中间件或原始 dispatch
};
store
: 当前 Store 实例(通过dispatch
和getState
访问)。next
: 下一个中间件的dispatch
方法(或原始dispatch
)。action
: 当前处理的 Action。
五、手动实现 Redux 中间件机制
1. 实现 applyMiddleware
函数
applyMiddleware
的作用是将多个中间件组合并增强 store.dispatch
。
javascript
function applyMiddleware(...middlewares) {
return (createStore) => (reducer) => {
// 1. 创建原始 Store
const store = createStore(reducer);
let dispatch = store.dispatch; // 原始 dispatch
// 2. 给每个中间件注入 store 的 dispatch 和 getState
const middlewareAPI = {
getState: store.getState,
dispatch: (action) => dispatch(action), // 初始时指向原始 dispatch
};
const chain = middlewares.map(middleware => middleware(middlewareAPI));
// 3. 组合中间件:从右到左嵌套调用
dispatch = compose(...chain)(store.dispatch);
// 4. 返回增强后的 Store
return {
...store,
dispatch, // 替换为增强后的 dispatch
};
};
}
// 工具函数:从右到左组合函数(类似 compose(f, g, h) = f(g(h(x))))
function compose(...funcs) {
if (funcs.length === 0) return arg => arg;
if (funcs.length === 1) return funcs[0];
return funcs.reduce((a, b) => (...args) => a(b(...args)));
}
2. 实现一个日志中间件
记录每个 Action 和更新后的 State:
javascript
const loggerMiddleware = ({ dispatch, getState }) => next => action => {
console.log('Dispatching:', action); // 打印 Action
const result = next(action); // 调用下一个中间件或原始 dispatch
console.log('Next State:', getState()); // 打印更新后的 State
return result; // 返回结果(可能是 Promise 等)
};
3. 实现一个异步中间件(如处理 Promise)
支持 dispatch
一个 Promise,并在 Promise 完成后自动触发真正的 Action:
javascript
const thunkMiddleware = ({ dispatch, getState }) => next => action => {
if (typeof action === 'function') {
// 如果 Action 是函数,则调用它并传入 dispatch 和 getState
return action(dispatch, getState);
}
// 否则直接传递给下一个中间件或原始 dispatch
return next(action);
};
六、完整示例:整合中间件
1. 创建 Store 并应用中间件
javascript
// 1. 原始 createStore(简化版)
function createStore(reducer) {
let state;
let listeners = [];
const getState = () => state;
const dispatch = (action) => {
state = reducer(state, action);
listeners.forEach(listener => listener());
};
const subscribe = (listener) => {
listeners.push(listener);
return () => listeners.filter(l => l !== listener);
};
dispatch({ type: '@@INIT' }); // 初始化 State
return { getState, dispatch, subscribe };
}
// 2. 应用中间件
const store = applyMiddleware(
loggerMiddleware,
thunkMiddleware
)(createStore)(counterReducer); // counterReducer 是之前定义的 Reducer
// 3. 使用示例
store.dispatch({ type: 'INCREMENT' }); // 日志中间件会打印 Action 和 State
store.dispatch(asyncAction()); // thunkMiddleware 会处理异步 Action
4. 异步 Action 示例
javascript
// 异步 Action Creator
function asyncAction() {
return (dispatch, getState) => {
setTimeout(() => {
dispatch({ type: 'INCREMENT' }); // 延迟触发真正的 Action
}, 1000);
};
}
七、Redux 官方中间件实现对比
Redux 官方的 applyMiddleware
实现更严谨(支持错误处理等),但核心思想与上述代码一致。官方源码通过闭包和函数组合实现了中间件的嵌套调用链。
八、关键总结
-
中间件的本质:
- 是一个高阶函数,通过包裹
dispatch
扩展其功能。 - 形如
store => next => action => { ... }
。
- 是一个高阶函数,通过包裹
-
中间件的执行顺序:
- 从右到左组合(类似洋葱模型),先定义的中间件后执行。
-
常见中间件:
redux-thunk
: 处理异步 Action(函数形式的 Action)。redux-logger
: 打印 Action 和 State 变化。redux-saga
: 基于 Generator 的复杂异步流程管理。
-
与 React-Redux 的关系:
react-redux
的connect
或 Hooks 不是中间件,而是连接组件和 Store 的桥梁。
通过理解中间件的原理,可以灵活扩展 Redux 的功能(如添加请求拦截、崩溃上报等)。