深入理解 Redux:从手写核心到现代实践(附 RTK 衔接)

Redux 作为前端经典状态管理库,核心解决 "组件间状态共享" 与 "状态变更可追溯" 问题,适配大型 React 项目。这篇文章我通过结合手写核心逻辑现代工具 RTK,拆解 Redux 原理,明晰 "底层原理" 与 "高效开发" 的协同路径

一、Redux 核心三要素:Action、Reducer、Store

Redux 遵循单向数据流 ,状态变更需经 Action → Reducer → Store 流程,确保可预测性。

1. Action:操作描述的 "消息载体"

Action 是触发状态变更的 "入口",本质是平面对象 (需 isPlainObject 验证),核心作用是描述 "发生了什么操作",可通过 payload 携带数据。

核心特性(结合手写代码)

  • 必须含 type 属性 :标识操作类型(如 INCREMENT),类型无强制约束(字符串、Symbol 均可),大型项目建议单独维护(如 actionTypes.js)避免硬编码。

    示例:

    js 复制代码
    // 基础 Action 结构
    const incrementAction = {
      type: "INCREMENT",
      payload: 1 
    };
  • Action 创建函数(Action Creator) :用纯函数 简化 Action 生成,函数无副作用(不修改参数、无异步、不影响外部环境)。

    示例:

    js 复制代码
    // 生成"增加计数"的 Action
    const increment = (step) => ({
      type: "INCREMENT",
      payload: step
    });
  • bindActionCreators:自动分发 Action

    手动创建 Action 需调用 store.dispatch(action) 触发变更,bindActionCreators 可增强 Action Creator,使其创建后自动分发 ,简化代码。

    核心实现(手写逻辑):

    js 复制代码
    function getAutodisPatchActionCreator(actionCreator, dispatch) {
      return (...args) => {
        const action = actionCreator(...args); 
        dispatch(action); 
      };
    }
    
    export default function bindActionCreators(actionCreators, dispatch) {
      if (typeof actionCreators === 'function') {
        return getAutodisPatchActionCreator(actionCreators, dispatch);
      }
      const result = {};
      for (const key in actionCreators) {
        const creator = actionCreators[key];
        if (typeof creator === 'function') {
          result[key] = getAutodisPatchActionCreator(creator, dispatch);
        }
      }
      return result;
    }

2. Reducer:状态计算的 "纯函数"

Reducer 是 Redux 的 "状态计算核心",接收当前 state 和 Action,返回新状态(不可修改原状态),决定 "如何根据操作变更状态"。

核心特性(结合手写代码)

  • 必须是纯函数:保障 Redux 可预测性,要求:

    • 不修改入参 stateaction
    • 无异步操作(如定时器、接口请求);
    • 不影响外部环境(如修改全局变量、操作 DOM)。
      优势:纯函数便于测试、可还原状态,且能优化 React 组件渲染(避免不必要重绘)。
  • 初始化状态的技巧

    创建 Store 时,Redux 自动分发初始化 Action (类型为 @@redux/INIT + 随机字符串,由 ActionTypes.INIT() 生成),此时 Reducer 接收 state = undefined,可通过参数默认值初始化状态:

    js 复制代码
    // 计数状态管理示例
    function countReducer(state = 0, action) {
      switch (action.type) {
        case "INCREMENT":
          return state + action.payload; 
        case "DECREMENT":
          return state - action.payload;
        default:
          return state; 
      }
    }
  • combineReducers:拆分复杂 Reducer

    大型项目状态结构复杂(如 "用户信息""购物车"),需按 "状态模块" 拆分 Reducer,再用 combineReducers 合并为根 Reducer

    核心实现(手写逻辑):

    js 复制代码
    export default function combineReducers(reducers) {
      validateReducers(reducers); 
    
      return function (state = {}, action) {
        const newState = {};
        for (const key in reducers) {
          if (reducers.hasOwnProperty(key)) {
            const subReducer = reducers[key];
            newState[key] = subReducer(state[key], action); 
          }
        }
        return newState; 
      };
    }

    示例(合并 "用户" 与 "计数" 模块):

    js 复制代码
    const rootReducer = combineReducers({
      user: userReducer, 
      count: countReducer 
    });
    // Store 状态结构:{ user: {...}, count: 0 }

3. Store:状态的 "统一容器"

Store 是 Redux 中 "唯一的状态容器",通过 createStore(reducer) 创建,封装状态存储、分发 Action、监听状态变化的核心能力。

核心方法(手写实现)

  • getState():获取当前状态

    直接返回 Store 内部保存的 currentState,是组件获取状态的唯一途径:

    js 复制代码
    function getState() {
      return currentState; 
    }
  • dispatch(action):触发状态变更

    唯一能修改状态的方法,流程:

    ① 验证 Action(必须是平面对象,且含 type);

    ② 调用 Reducer,传入当前状态和 Action,计算新状态;

    ③ 执行所有监听器(listeners),通知状态变更。

    核心代码:

    js 复制代码
    function dispatch(action) {
      if (!isPlainObject(action)) {
        throw new TypeError("action must be a plain object");
      }
      if (action.type === undefined) {
        throw new TypeError("action must has a property of type");
      }
    
      currentState = currentReducer(currentState, action); 
    
      for (const listener of listeners) {
        listener(); 
      }
    }
  • subscribe(listener):监听状态变化

    注册 "状态变更监听器"(无参函数),dispatch 触发后执行所有监听器;返回 "取消监听" 函数,避免内存泄漏。

    核心代码:

    js 复制代码
    function subscribe(listener) {
      listeners.push(listener); 
      let isRemoved = false;
    
      return function () {
        if (isRemoved) return;
        const index = listeners.indexOf(listener);
        listeners.splice(index, 1); 
        isRemoved = true;
      };
    }
  • Store 初始化

    创建 Store 时,自动分发初始化 ActionActionTypes.INIT()),触发 Reducer 执行,通过参数默认值完成状态初始化(无需手动传默认状态)。

二、Redux 工具函数与中间件

1. 工具函数:保障核心逻辑合法性

  • isPlainObject(obj) :验证平面对象(__proto__ 指向 Object.prototype),避免非标准对象(如数组、类实例)导致逻辑异常。

    js 复制代码
    export default function isPlainObject(obj) {
      if (typeof obj !== "object") return false;
      return Object.getPrototypeOf(obj) === Object.prototype;
    }
  • ActionTypes :生成随机初始化 Action 类型(如 @@redux/INIT + 随机字符串),避免与业务 Action 冲突。

    js 复制代码
    function getRandom(length) {
      return Math.random().toString(36).slice(2, length + 2).split("").join(".");
    }
    
    export default {
      INIT() { return `@@redux/INIT${getRandom(6)}`; },
      UNKNOWN() { return `@@redux/PROBE_UNKNOWN_ACTION${getRandom(6)}`; }
    };

2. 中间件:增强 dispatch 能力

Redux 核心仅支持同步 Action ,处理异步操作(如接口请求)需通过中间件 扩展 dispatch 功能。

中间件本质是 "函数链",在 dispatch 触发 Action 后、Reducer 执行前,插入自定义逻辑(如异步请求、日志、错误捕获)。常见中间件:

  • redux-thunk:支持函数式 Action(Action 可返回函数,内部执行异步操作);

  • redux-saga:用 Generator 管理复杂异步流程(如接口重试、竞态处理)。

核心原理 :中间件包装 store.dispatch,支持非标准 Action(如函数、Promise),待异步完成后,分发真正的 "同步 Action" 触发状态变更。

三、从手写 Redux 到 RTK:现代开发的演进

现代开发中,Redux 官方推出 Redux Toolkit(RTK) ,封装 createSlice(合并 Action 与 Reducer)、configureStore(简化 Store 创建,内置中间件)等工具,大幅减少重复代码(无需手动写 combineReducersbindActionCreators),已成为 Redux 开发的推荐方案

手写核心 vs RTK:定位与价值

  • 手写核心 :并非替代 RTK,而是为了深入理解 Redux 底层逻辑。掌握核心原理后,能更灵活应对 RTK 封装的 "黑盒场景"(如自定义中间件、调试复杂状态流)。
  • RTK :基于 Redux 核心逻辑的高效工具 ,让开发更简洁(如 createSlice 自动生成 Action 与 Reducer),适合快速迭代的项目。

RTK 关键工具示例

  • createSlice :合并 Action 类型、Reducer、Action Creator,自动生成 Action(如 increment)。

    js 复制代码
    import { createSlice } from '@reduxjs/toolkit';
    
    const counterSlice = createSlice({
      name: 'counter',
      initialState: 0,
      reducers: {
        increment: (state, action) => state + action.payload,
        decrement: (state, action) => state - action.payload,
      },
    });
    
    // 自动生成 Action Creator:counterSlice.actions.increment
  • configureStore :简化 Store 创建,内置 redux-thunkredux-devtools 等中间件。

    js 复制代码
    import { configureStore } from '@reduxjs/toolkit';
    
    const store = configureStore({
      reducer: rootReducer, 
    });

四、Redux 工作流程与核心价值

1. 完整流程(手写与 RTK 通用)

  1. 组件调用 Action Creator(或 RTK 的 slice.actions)触发 Action;
  2. dispatch 传递 Action 至 Reducer;
  3. Reducer 计算新状态,更新 Store;
  4. 监听器(如 React 组件的重新渲染逻辑)触发组件更新,读取新状态(store.getState())。

2. 核心价值

Redux 通过分离 Action、Reducer、Store ,让状态变更可追溯、可测试,尤其适配大型项目的复杂状态管理。

  • 手写核心:是理解 Redux 设计思想的 "最佳路径",掌握后能精准定位状态问题;
  • RTK:是高效开发的 "助推器",让 Redux 从 "繁琐配置" 转向 "简洁实践"。

五、总结

Redux 的核心价值在于规范状态管理流程,手写实现揭示其 "简洁而严谨" 的逻辑(纯函数、单向数据流);RTK 则是站在 Redux 肩膀上的 "现代工具",让开发更高效。

对于我们开发者而言:

  • 新手可先通过手写核心理解原理,再迁移到 RTK 简化开发;

  • 进阶者需兼顾 "底层逻辑" 与 "工具应用",让 Redux 真正服务于项目稳定性与可维护性。

二者相辅相成,共同构建前端状态管理的 "清晰脉络"。


如果您觉得这篇文章对您有帮助,欢迎点赞和收藏,大家的支持是我继续创作优质内容的动力🌹🌹🌹也希望您能在😉😉😉我的主页 😉😉😉找到更多对您有帮助的内容。

  • 致敬每一位赶路人
相关推荐
秋名山大前端17 分钟前
Chrome GPU 加速优化配置(前端 3D 可视化 / 数字孪生专用)
前端·chrome·3d
今天不要写bug19 分钟前
antv x6实现封装拖拽流程图配置(适用于工单流程、审批流程应用场景)
前端·typescript·vue·流程图
luquinn19 分钟前
实现统一门户登录跳转免登录
开发语言·前端·javascript
用户214118326360238 分钟前
dify案例分享-5分钟搭建智能思维导图系统!Dify + MCP工具实战教程
前端
augenstern41642 分钟前
HTML(面试)
前端
excel1 小时前
前端常见布局误区:1fr 为什么撑爆了我的容器?
前端
烛阴1 小时前
TypeScript 类型魔法:像遍历对象一样改造你的类型
前端·javascript·typescript
vayy1 小时前
uniapp中 ios端 scroll-view 组件内部子元素z-index失效问题
前端·ios·微信小程序·uni-app
专注API从业者1 小时前
基于 Node.js 的淘宝 API 接口开发:快速构建异步数据采集服务
大数据·前端·数据库·数据挖掘·node.js
前端无冕之王1 小时前
一份兼容多端的HTML邮件模板实践与详解
前端·css·数据库·html