首页
/ Redux 入门指南:安装、核心数据流与 createStore 源码级验证

Redux 入门指南:安装、核心数据流与 createStore 源码级验证

2026-09-04 16:25:33作者:董灵辛Dennis

本文基于 Redux 仓库官方 Getting Started 文档,系统讲解 Redux 的入门路径:如何安装 Redux Toolkit(RTK)与 Redux 核心包、如何用官方模板创建新项目、如何用 createSlice/configureStorecreateStore 写出第一个计数器应用,并对照当前仓库的 src 源码逐一验证「store、action、reducer」三大数据流概念的真实实现,帮助读者既会写代码,也懂底层原理。

Redux 单向数据流示意图:View 发出 Actions,Actions 更新 State,State 再驱动 View 重新渲染

一、Redux 是什么:可预测的全局状态管理库

Redux 是一个用于可预测(predictable)且可维护(maintainable)的全局状态管理的 JavaScript 库。它帮助你编写行为一致、可运行于不同环境(浏览器、服务端、原生端)且易于测试的应用,并提供优秀的开发体验——例如结合实时代码编辑与时间旅行调试器(time traveling debugger)。

Redux 可以与 React 一起使用,也可以与任何其他视图库搭配。它非常小巧(约 2kB,含依赖),但拥有庞大的生态插件体系。

官方推荐立场(重要):Redux 官方明确推荐 Redux Toolkit(RTK) 作为编写 Redux 逻辑的首选方案。RTK 包裹在 Redux 核心之上,内置了官方建议的最佳实践,包含一系列构建 Redux 应用所必需的包与函数,它能简化大多数 Redux 任务、防止常见错误、降低编写 Redux 应用的难度。RTK 内置的实用工具覆盖了多种常见场景,包括:

  • Store 配置configureStore 封装了 store 搭建;
  • Reducer 与不可变更新createReducer 帮助创建 reducer 并编写 immutable 更新逻辑;
  • 状态切片createSlice 可以一次性创建整个状态切片(reducer + 对应 action creator)。

无论你是刚搭建第一个项目的 Redux 新手,还是希望简化既有应用的资深用户,RTK 都能让你的 Redux 代码更好。仓库中专门有一篇文档阐述「为什么 RTK 就是今天的 Redux」,见 why-rtk-is-redux-today.md

二、安装

2.1 安装 Redux Toolkit

Redux Toolkit 以 NPM 包的形式发布,可用于模块打包器(Vite、Webpack 等)或 Node 应用中:

# NPM
npm install @reduxjs/toolkit

# Yarn
yarn add @reduxjs/toolkit

2.2 创建新的 Redux 项目

启动新项目时,官方推荐从官方模板入手。这些模板预配置好了 Redux Toolkit,并附带一个小的示例应用供你起步。可以用 tiged 这类工具来克隆并解压模板,文档给出的完整命令如下:

# Vite + TypeScript
npx tiged reduxjs/redux-templates/packages/vite-template-redux my-app

# Create React App + TypeScript
npx tiged reduxjs/redux-templates/packages/cra-template-redux-typescript my-app

# Create React App + JavaScript
npx tiged reduxjs/redux-templates/packages/cra-template-redux my-app

# Expo + TypeScript
npx tiged reduxjs/redux-templates/packages/expo-template-redux-typescript my-app

# React Native + TypeScript
npx tiged reduxjs/redux-templates/packages/react-native-template-redux-typescript my-app

# Standalone Redux Toolkit App Structure Example
npx tiged reduxjs/redux-templates/packages/rtk-app-structure-example my-app

除官方模板外,社区也提供了其他模板,例如 Next.js 的 with-redux 模板:

# Next.js + Redux
npx create-next-app --example with-redux my-app

更多安装细节(如 React-Redux 配套包、Redux DevTools 浏览器扩展的安装)可参见仓库内的 Installation 文档

2.3 安装 Redux 核心包

Redux 核心库同样以 NPM 包发布,可用于模块打包器或 Node 应用:

# NPM
npm install redux

# Yarn
yarn add redux

从仓库根目录的 package.json 可以看到,当前发布的 redux 包版本为 5.0.1,它同时提供 CJS 与 ESM 构建产物:

"main": "dist/cjs/redux.cjs",
"module": "dist/redux.legacy-esm.js",
"exports": {
  ".": {
    "types": "./dist/redux.d.mts",
    "import": "./dist/redux.mjs",
    "default": "./dist/cjs/redux.cjs"
  }
}

并且声明了 "sideEffects": false,意味着打包器可以放心做 tree-shaking——这也是文档称其「tiny(2kB 级)」的构建层面原因。该包还包含一个预编译的 ESM 构建(redux.browser.mjs),可以直接在浏览器里通过 <script type="module"> 标签使用。仓库中的 examples/counter-vanilla/index.html 就是一个真实演示:它直接用 <script type="module"> 引入浏览器版 ESM 构建,定义一个 counter(state, action) reducer,然后 Redux.createStore(counter) 创建 store、store.subscribe(render) 订阅变化、点击按钮时 store.dispatch({ type: 'INCREMENT' }) 触发状态更新——无需任何打包器,完整验证了文档中「Basic Example」描述的最小可用闭环。

三、基础示例:三大核心概念(store / action / reducer)

Redux 的数据流规则可以概括为三句话:

  1. 应用的全部全局状态以对象树的形式存放在单一 store 中;
  2. 修改状态树的唯一方式是创建 action(一个描述「发生了什么」的对象)并将其 dispatch 到 store;
  3. 通过编写纯函数 reducer 来规定状态如何响应 action 更新:根据旧状态和 action 计算出新状态。

3.1 Redux Toolkit 写法

RTK 简化了编写 Redux 逻辑和配置 store 的过程。用 RTK 编写的基础应用逻辑如下(该片段与 examples/counter 示例项目的写法一致):

import { createSlice, configureStore } from '@reduxjs/toolkit'

const counterSlice = createSlice({
  name: 'counter',
  initialState: {
    value: 0
  },
  reducers: {
    incremented: state => {
      // Redux Toolkit allows us to write "mutating" logic in reducers. It
      // doesn't actually mutate the state because it uses the Immer library,
      // which detects changes to a "draft state" and produces a brand new
      // immutable state based off those changes
      state.value += 1
    },
    decremented: state => {
      state.value -= 1
    }
  }
})

export const { incremented, decremented } = counterSlice.actions

const store = configureStore({
  reducer: counterSlice.reducer
})

// Can still subscribe to the store
store.subscribe(() => console.log(store.getState()))

// Still pass action objects to `dispatch`, but they're created for us
store.dispatch(incremented())
// {value: 1}
store.dispatch(incremented())
// {value: 2}
store.dispatch(decremented())
// {value: 1}

关键点:

  • state.value += 1 这种「看似修改」的写法是安全的。RTK 底层使用 Immer 库:它检测对「draft state」的改动,并据此生成一个全新的 immutable 状态对象;
  • createSlice 一次性生成 reducer 和对应的 action creator(incremented()decremented()),你仍然向 dispatch 传递 action 对象,只是这些对象由 RTK 替你创建;
  • store.subscribe()store.getState() 的用法与核心 Redux 完全一致。

仓库中更完整的 RTK 切片示例见 examples/counter/src/features/counter/counterSlice.js:其中除了同步 reducers 之外,还展示了 extraReducers 处理 createAsyncThunk 生成的 pending/fulfilled 动作、selector(selectCount)以及手写 thunk(incrementIfOdd);而 examples/counter/src/app/store.js 则展示了应用长大后的标准 store 形态——configureStore({ reducer: { counter: counterReducer } }),即用 reducer map 而不是单个 reducer。

3.2 单一 store 与 reducer 拆分

在典型的 Redux 应用中,只有一个 store一个根 reducer 函数。随着应用增长,你把根 reducer 拆分为多个小 reducer,各自独立地处理状态树的不同部分——这与 React 应用中只有一个根组件、但由许多小组件组合而成完全类似。

「应用只有一个 store」这一点在核心源码中有明确体现。src/createStore.ts 的 JSDoc 写道:

The only way to change the data in the store is to call dispatch() on it. There should only be a single store in your app. To specify how different parts of the state tree respond to actions, you may combine several reducers into a single reducer function by using combineReducers.

「拆分 reducer」对应仓库中的 src/combineReducers.ts。它在初始化时会执行 assertReducerShape:对每个 slice reducer 调用 reducer(undefined, { type: ActionTypes.INIT }),如果返回 undefined 就直接抛错——这就是 Redux 著名的「reducer 在 state 为 undefined 时必须显式返回初始状态」规则的强制点。文档 Basic Example 中 counterReducer(state = { value: 0 }, action)state 设置默认参数,正是为了通过这一校验。

3.3 Legacy 写法(无抽象的原始 Redux)

作为对照,不使用任何抽象的原始 Redux 语法(legacy 写法)如下:

import { createStore } from 'redux'

/**
 * This is a reducer - a function that takes a current state value and an
 * action object describing "what happened", and returns a new state value.
 * A reducer's function signature is: (state, action) => newState
 *
 * The Redux state should contain only plain JS objects, arrays, and primitives.
 * The root state value is usually an object. It's important that you should
 * not mutate the state object, but return a new object if the state changes.
 *
 * You can use any conditional logic you want in a reducer. In this example,
 * we use a switch statement, but it's not required.
 */
function counterReducer(state = { value: 0 }, action) {
  switch (action.type) {
    case 'counter/incremented':
      return { value: state.value + 1 }
    case 'counter/decremented':
      return { value: state.value - 1 }
    default:
      return state
  }
}

// Create a Redux store holding the state of your app.
// Its API is { subscribe, dispatch, getState }.
let store = createStore(counterReducer)

// You can use subscribe() to update the UI in response to state changes.
// Normally you'd use a view binding library (e.g. React Redux) rather than subscribe() directly.
// There may be additional use cases where it's helpful to subscribe as well.

store.subscribe(() => console.log(store.getState()))

// The only way to mutate the internal state is to dispatch an action.
// The actions can be serialized, logged or stored and later replayed.
store.dispatch({ type: 'counter/incremented' })
// {value: 1}
store.dispatch({ type: 'counter/incremented' })
// {value: 2}
store.dispatch({ type: 'counter/decremented' })
// {value: 1}

与 RTK 版本相比,legacy 写法中:状态不能被直接修改,你只能用称为 action 的普通对象来「声明想发生的变更」;然后编写一个 reducer 函数,决定每个 action 如何转换整个应用状态。

3.4 为什么这种「看似繁琐」的架构值得

这个架构对于一个计数器应用来说可能显得多余,但它的价值在于能平滑扩展到大型复杂应用,并且能支撑非常强大的开发者工具——因为每一次状态变更都可以追溯到触发它的 action。你可以录制用户的操作会话,仅通过重放(replay)每一个 action 就能完整复现。Redux Toolkit 让你在保持相同 Redux 行为与数据流的前提下,写出更短、更易读的逻辑。

四、源码级验证:createStore 的 dispatch 校验与执行流程

文档中的三句话规则在 src/createStore.ts 中得到了逐条印证,以下摘录可直接对照阅读:

1. store 创建即派发一个 INIT action。创建 store 的最后一步是:

// When a store is created, an "INIT" action is dispatched so that every
// reducer returns their initial state. This effectively populates
// the initial state tree.
dispatch({ type: ActionTypes.INIT } as A)

也就是说,counterReducer 的初始值 { value: 0 } 正是在 store 构造时由这个内部 action「喂」给 reducer 得到的——这就是 reducer 默认参数 state = { value: 0 } 的意义。

2. dispatch 只接受普通对象 action,且 type 必须是字符串src/createStore.ts):

function dispatch(action: A) {
  if (!isPlainObject(action)) {
    throw new Error(
      `Actions must be plain objects. Instead, the actual type was: '${kindOf(action)}'. ...`
    )
  }
  if (typeof action.type === 'undefined') {
    throw new Error(
      'Actions may not have an undefined "type" property. You may have misspelled an action type string constant.'
    )
  }
  // ...type 必须为 string,且 reducer 执行期间不允许再 dispatch

这对应了文档中「action 是描述发生了什么的对象」这一约束:action 必须是 plain object(可用 src/utils/isPlainObject.ts 复核判定逻辑)、type 不能为 undefined 且必须是字符串——这也是官方建议「用字符串常量作为 action type」的底层原因。要 dispatch Promise、Observable、thunk 之类的值,则必须通过中间件(如 redux-thunk)扩展 dispatch,文档在 src/createStore.ts 的注释中明确了这一点。

3. reducer 执行期间禁止读取/订阅状态src/createStore.ts):getState() 内部检查 isDispatching 标志,若在 reducer 执行期间调用会直接抛错「You may not call store.getState() while the reducer is executing」;subscribe() 与取消订阅在 dispatch 进行中同样被禁止(src/createStore.ts)。而订阅列表采用快照机制:每次 dispatch 前由 ensureCanMutateNextListeners() 浅拷贝一份监听器 Map(src/createStore.ts),确保 dispatch 过程中新增/移除的订阅不影响正在进行的这一次派发——这正是「订阅在每次 dispatch 前被快照」这一文档级承诺的实现方式。

4. reducer 替换与可观察性互操作。store 还提供 replaceReducersrc/createStore.ts),通过内部派发 ActionTypes.REPLACE 让新旧 reducer 都收到旧状态,实现状态树的无缝迁移——文档中 Code Splitting、热重载等进阶用法都依赖此 API;以及 Symbol.observable 的 observable() 方法(src/createStore.ts),使 store 可与 RxJS 等响应式库互操作。

5. createStore 的弃用立场与 legacy_createStore。在 src/createStore.ts 的 JSDoc 中,核心库明确标注 createStore@deprecated,推荐改用 RTK 的 configureStore,并说明:如果你想在 TypeScript 编辑器中不显示弃用警告,可导入 legacy_createStore 并别名为 createStore 使用(import { legacy_createStore as createStore } from 'redux')。两者在 src/index.ts 中一同导出。换言之:

  • 学习原理:用 createStore(legacy 写法),它「不会移除,但官方鼓励所有用户迁移到 RTK」;
  • 生产项目:用 configureStore,它内置了开发者模式校验、中间件组合等能力。

6. Redux 核心导出的全部 API。从 src/index.ts 可以确认,redux 核心包导出的函数仅有:createStorelegacy_createStorecombineReducersbindActionCreatorsapplyMiddlewarecomposeisActionisPlainObject,以及供类型系统使用的 StoreReducerActionMiddleware 等类型。其中 compose 的实现见 src/compose.ts——从右到左组合单参数函数,是 store enhancer(如 applyMiddleware 内部)组合的基础构件。

五、学习路径与配套资源

文档为不同阶段的学习者规划了清晰的资源地图,均可在当前仓库内找到入口:

5.1 Redux Essentials 教程(自顶向下)

Redux Essentials 教程 是「top-down」教程,教你「以正确的方式使用 Redux」,采用最新推荐的 API 与最佳实践。官方建议从这里开始。共 8 部分:概述概念、应用结构、数据流、使用数据、异步逻辑、性能与数据归一化、RTK Query 基础与进阶(见 docs/tutorials/essentials 目录下各 part 文件)。

5.2 Redux Fundamentals 教程(自底向上)

Redux Fundamentals 教程 是「bottom-up」教程,从第一性原理出发、不引入任何抽象地讲解「Redux 如何工作」以及标准用法模式为何存在。共 8 部分,覆盖概念与数据流、状态/Action/Reducer、Store、UI 与 React、异步逻辑、标准模式与现代 Redux 写法。想理解本文第四节的源码行为,这条线是最优补充。

5.3 Learn Modern Redux 直播与示例

Redux 维护者 Mark Erikson 曾在 "Learn with Jason" 节目中讲解现代 Redux 用法,包含现场编码的示例应用,演示了如何用 Redux Toolkit、React-Redux hooks 与 TypeScript 组合,以及 RTK Query 数据获取 API(对应仓库文档 docs/tutorials/videos.md 中的资源介绍)。

5.4 仓库内置示例项目

Redux 仓库自带多个示例项目,演示 Redux 的各方面用法,几乎每个示例都有可在线交互的 CodeSandbox 版本。完整列表见 Examples 页面。结合仓库实际目录,各示例的侧重点如下:

5.5 其他文档资源

  • Redux FAQ 回答了大量关于 Redux 用法的常见疑问;「Using Redux」文档区docs/usage/index.md)涵盖派生数据、测试、reducer 逻辑组织与减少样板代码等主题;
  • Redux 维护者的 "Practical Redux" 教程系列演示 React + Redux 的中级/高级实战技巧;
  • 社区创建了数以千计的 Redux 相关库、插件与工具,官方推荐见 「Ecosystem」文档页

六、获取帮助与讨论

  • 官方答疑渠道是 Reactiflux Discord 社区的 #redux 频道,适合所有学习与使用相关的问题;
  • 也可在 Stack Overflow 使用 redux 标签提问;
  • 如有 bug 报告或反馈,请在 Redux 的 GitHub 仓库提交 issue。仓库内的 CONTRIBUTING.md 提供了贡献流程说明。

七、你应该使用 Redux 吗?

Redux 是组织状态的一个有价值工具,但你应当评估它是否适合你的具体场景。不要因为「别人说该用」就用 Redux——花时间理解使用它的潜在收益与代价。文档给出的建议是,以下情况适合使用 Redux:

  • 你有相当数量随时间变化的数据;
  • 你需要一个状态的单一数据源(single source of truth);
  • 你发现把所有状态都放在顶层组件里已经不够用了。

关于「何时该用 Redux」的更深入讨论,可继续阅读仓库内的 FAQ:When should I use Redux?Redux FAQ 总览

八、小结

主题 关键结论 仓库内依据
官方推荐方案 新代码一律用 RTK(configureStore/createSlice),核心 createStore 已标记弃用 docs/introduction/GettingStarted.mdsrc/createStore.ts
核心 API 面 核心包仅导出 createStore、combineReducers、bindActionCreators、applyMiddleware、compose 等 8 个函数 src/index.ts
数据流铁律 dispatch 只接受 plain object 且 type 为非 undefined 字符串;reducer 执行期禁读/订阅 src/createStore.ts
初始状态来源 store 构造时派发 INIT action,由 reducer 默认参数提供初始状态 src/createStore.ts
无打包器用法 ESM 构建可直接以 <script type="module"> 在浏览器运行 examples/counter-vanilla/index.html
学习路线 Essentials(自顶向下,推荐起点)+ Fundamentals(自底向上)+ 仓库 examples docs/tutorials/essentialsexamples/

完成本文的安装步骤、跑通 3.1/3.3 两个计数器示例、并按第五节路线读完 Essentials 教程后,你就同时掌握了 Redux 的「正确用法」与「底层原理」,可以开始构建自己的 Redux 应用了。

登录后查看全文
热门项目推荐
相关项目推荐

项目优选

收起
kernelkernel
deepin linux kernel
C
33
18
ops-transformerops-transformer
本项目是CANN提供的transformer类大模型算子库,实现网络在NPU上加速计算。
C++
1.12 K
2.72 K
ops-nnops-nn
本项目是CANN提供的神经网络类计算算子库,实现网络在NPU上加速计算。
C++
904
1.82 K
docsdocs
暂无描述
Markdown
889
5.78 K
pytorchpytorch
作为 Ascend for PyTorch 社区的核心组件,TorchNPU 是昇腾专为 PyTorch 打造的深度学习适配插件,使 PyTorch 框架能够直接调用昇腾 NPU,为开发者提供昇腾 AI 处理器的超强算力。
Python
854
1.34 K
kernelkernel
openEuler内核是openEuler操作系统的核心,既是系统性能与稳定性的基石,也是连接处理器、设备与服务的桥梁。
C
527
590
jiuwenswarmjiuwenswarm
JiuwenSwarm 是一款基于openJiuwen开发的智能AI Agent,它能够将大语言模型的强大能力,通过你日常使用的各类通讯应用,直接延伸至你的指尖。
Python
3.52 K
1.01 K
ops-mathops-math
本项目是CANN提供的数学类基础计算算子库,实现网络在NPU上加速计算。
C++
1.33 K
1.45 K
AscendNPU-IRAscendNPU-IR
AscendNPU-IR是基于MLIR(Multi-Level Intermediate Representation)构建的,面向昇腾亲和算子编译时使用的中间表示,提供昇腾完备表达能力,通过编译优化提升昇腾AI处理器计算效率,支持通过生态框架使能昇腾AI处理器与深度调优
C++
540
384
flutter_flutterflutter_flutter
本仓库是 Flutter SDK 与 Flutter Engine 的 OpenHarmony 适配版本,由 CPF-Flutter 团队维护。开发者可使用熟悉的 Flutter 技术栈开发 OpenHarmony 应用,3.35.7 及以后的适配版本可基于本仓库源码构建支持 OpenHarmony 的 Flutter Engine。
Dart
1.17 K
341