React
Redux 中间件的实现原理
Redux 中间件通过函数组合和柯里化对 dispatch 方法进行增强,实现了一个洋葱模型的执行流程,是 Redux 异步处理和功能扩展的核心机制。
一、中间件的本质
Redux 中间件是一个高阶函数,遵循柯里化的三层函数签名:
const middleware = (store: any) => (next: any) => (action: any) => {
// 在调用 next 之前执行逻辑(下行)
// 在调用 next 之后执行逻辑(上行)
return next(action)
}三层参数的含义:
store:Redux store,包含 getState 和 dispatchnext:下一个中间件或原始 dispatchaction:被 dispatch 的 action
二、applyMiddleware 的实现
applyMiddleware 是 Redux 提供的中间件应用器,它通过函数组合将多个中间件编织成一条链。
简化实现
function applyMiddleware(...middlewares: any[]) {
return (createStore: any) => (...args: any[]) => {
const store = createStore(...args)
let dispatch = store.dispatch
// 中间件可访问的 API
const middlewareAPI = {
getState: store.getState,
dispatch: (action: any) => dispatch(action),
}
// 给每个中间件注入 store API
const chain = middlewares.map((middleware) => middleware(middlewareAPI))
// 组合所有中间件,形成链条
dispatch = compose(...chain)(store.dispatch)
return {
...store,
dispatch,
}
}
}compose 函数的实现
function compose(...funcs: Function[]): Function {
if (funcs.length === 0) return (arg: any) => arg
if (funcs.length === 1) return funcs[0]
// 从右到左组合函数:f(g(x)) 表示先执行 g,再执行 f
return funcs.reduce(
(a: Function, b: Function) => (...args: any[]) => a(b(...args))
)
}三、执行流程(洋葱模型)
多个中间件的执行遵循洋葱模型:先从外到内逐层进入,遇到 next 后再从内到外逐层返回。
执行顺序示例
const store = createStore(reducer, applyMiddleware(middleware1, middleware2))
// 当 dispatch 一个 action 时的执行顺序:
// 1. middleware1 的前半部分(next 之前)
// 2. middleware2 的前半部分(next 之前)
// 3. 原始 reducer 处理 action
// 4. middleware2 的后半部分(next 之后)
// 5. middleware1 的后半部分(next 之后)四、常见中间件示例
日志中间件
const logger = (store: any) => (next: any) => (action: any) => {
console.log('dispatching:', action)
const result = next(action)
console.log('next state:', store.getState())
return result
}异步处理中间件(Thunk)
const thunk = (store: any) => (next: any) => (action: any) => {
// action 是函数时,调用它并传入 dispatch 和 getState
if (typeof action === 'function') {
return action(store.dispatch, store.getState)
}
// action 是普通对象,传递给下一个中间件
return next(action)
}
// 使用方式
const fetchUser = (id: number) => (dispatch: any, getState: any) => {
dispatch({ type: 'FETCH_USER_START' })
return fetch(`/api/users/${id}`)
.then((res) => res.json())
.then((data) =>
dispatch({ type: 'FETCH_USER_SUCCESS', payload: data })
)
.catch((err) =>
dispatch({ type: 'FETCH_USER_ERROR', payload: err })
)
}
store.dispatch(fetchUser(1))条件中间件(只在满足条件时执行)
const conditionalMiddleware = (condition: boolean) => (store: any) => (
next: any
) => (action: any) => {
if (condition) {
console.log('action:', action)
}
return next(action)
}五、中间件的组合
import { createStore, applyMiddleware } from 'redux'
const store = createStore(
reducer,
applyMiddleware(
thunk, // 第一个执行
logger, // 第二个执行
errorHandler // 第三个执行
)
)中间件执行顺序由传入 applyMiddleware 的顺序决定。通常的最佳实践是:异步中间件(如 thunk)放在最前,日志和监控中间件放在后面。
六、注意事项
必须调用 next(action)
不调用 next 会中断 action 的传递,导致后续中间件和 reducer 无法执行。
// ✓ 正确:调用 next 继续传递
const middleware = (store: any) => (next: any) => (action: any) => {
console.log(action)
return next(action)
}
// ✗ 错误:中断了 action 的传递
const middleware = (store: any) => (next: any) => (action: any) => {
console.log(action)
// 没有调用 next,action 不会继续传递
}中间件内避免直接修改状态
中间件应该是纯函数,通过 dispatch 新的 action 来修改状态,而不是直接修改。
// ✗ 错误:直接修改
const middleware = (store: any) => (next: any) => (action: any) => {
store.getState().user.name = 'newName' // 禁止
return next(action)
}
// ✓ 正确:dispatch 新 action
const middleware = (store: any) => (next: any) => (action: any) => {
if (needsSpecialHandling(action)) {
store.dispatch({ type: 'UPDATE_NAME', payload: 'newName' })
}
return next(action)
}中间件的顺序很重要
不同顺序会导致完全不同的执行流程和结果。
// 这两种顺序会产生不同的行为
applyMiddleware(thunk, logger) // thunk 先执行
applyMiddleware(logger, thunk) // logger 先执行核心机制:中间件通过函数组合实现洋葱模型,
applyMiddleware负责链接各个中间件,compose负责函数嵌套。每个中间件都会被注入 store 的 API,可以在 dispatch 前后执行逻辑,必须调用next(action)才能继续传递。