ByAI:Redux及中间件原理和实现

Redux 是一个用于 JavaScript 应用的状态管理库,其核心思想是通过单一数据源(Store)​不可变状态纯函数(Reducer)​来管理应用状态。以下是 Redux 的核心原理及一个简化实现:


一、Redux 核心原理

  1. Store

    • 全局唯一的状态容器,通过 createStore 创建。
    • 提供 getState() 获取当前状态、dispatch(action) 触发状态更新、subscribe(listener) 订阅状态变化。
  2. Action

    • 普通的 JavaScript 对象,必须包含 type 字段,描述要执行的操作(如 { type: 'INCREMENT' })。
  3. Reducer

    • 纯函数,接收当前状态和 Action,返回新的状态(state = reducer(state, action))。
    • 必须是不可变的(每次返回新对象,而非直接修改原状态)。
  4. 数据流

    • 单向数据流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();

三、关键点解析

  1. 初始化 State

    • 首次调用 dispatch({ type: '@@INIT' }),触发 Reducer 返回初始状态。
  2. 不可变更新

    • Reducer 必须返回新对象(如 { ...state, count: state.count + 1 }),而不是直接修改原状态。
  3. 订阅机制

    • subscribe 将监听函数存入数组,dispatch 后遍历调用所有监听函数。
  4. 简化假设

    • 省略了中间件(如 redux-thunk)、异步 Action、CombineReducers 等功能。

Redux 中间件(Middleware)的核心作用是dispatch 发起 Action 到 Reducer 处理 Action 的过程中,插入自定义逻辑 ​(如异步操作、日志记录、错误处理等)。它的本质是一个函数增强器 ,通过层层嵌套包裹原始的 dispatch 方法,扩展其功能。


四、Redux 中间件原理

1. 中间件的核心目标

  • dispatch(action) 执行前后插入自定义逻辑(如异步请求、日志打印)。
  • 不修改 Redux 核心代码,通过组合的方式扩展 dispatch

2. 中间件的执行流程

  1. 用户调用 store.dispatch(action)
  2. 中间件拦截 dispatch,执行自定义逻辑(如发起异步请求)。
  3. 中间件决定是否继续调用下一个中间件或最终的 dispatch
  4. 最终调用原始的 dispatch 触发 Reducer 更新状态。

3. 中间件的签名

中间件是一个三参数函数 ,接收 storedispatchgetState 方法,返回一个新的 dispatch 函数:

javascript 复制代码
const middleware = ({ dispatch, getState }) => next => action => {
  // 自定义逻辑(如异步、日志)
  return next(action); // 调用下一个中间件或原始 dispatch
};
  • store: 当前 Store 实例(通过 dispatchgetState 访问)。
  • 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 实现更严谨(支持错误处理等),但核心思想与上述代码一致。官方源码通过闭包和函数组合实现了中间件的嵌套调用链。


八、关键总结

  1. 中间件的本质​:

    • 是一个高阶函数,通过包裹 dispatch 扩展其功能。
    • 形如 store => next => action => { ... }
  2. 中间件的执行顺序​:

    • 从右到左组合(类似洋葱模型),先定义的中间件后执行。
  3. 常见中间件​:

    • redux-thunk: 处理异步 Action(函数形式的 Action)。
    • redux-logger: 打印 Action 和 State 变化。
    • redux-saga: 基于 Generator 的复杂异步流程管理。
  4. 与 React-Redux 的关系​:

    • react-reduxconnect 或 Hooks 不是中间件,而是连接组件和 Store 的桥梁。

通过理解中间件的原理,可以灵活扩展 Redux 的功能(如添加请求拦截、崩溃上报等)。

相关推荐
HarderCoder5 小时前
ByAI:Redux中间件的原理和ts简化实现
前端·redux
市民中心的蟋蟀8 天前
第九章 案例 3 - Valtio 【上】
前端·react.js·redux
gaog2zh1 个月前
0903Redux改造项目_用户信息_状态管理-react-仿低代码平台项目
react.js·redux
Yvette-W2 个月前
【更新中】【React】基础版React + Redux实现教程(Vite + React + Redux + TypeScript)
前端·javascript·react.js·typescript·前端框架·vite·redux
aklry2 个月前
redux的使用
react.js·redux
aklry2 个月前
react项目中使用redux
react.js·redux
黑白灰223 个月前
从 0 到 1 打造高颜值 React 云音乐,看完这篇就够了!
react.js·redux
dleei3 个月前
RTK的基本使用
前端·react.js·redux
FE_C_P小麦3 个月前
使用 Redux Toolkit封装一个模块化的 store
react.js·typescript·redux