Redux 入门超详细指南
实习时突然遇到一个问题,一个页面中用到了将近 10 个组件,传参传来传去复杂得很,所以就有了这篇临时学习的 Redux 入门指南。
一、非常重要的 引言
在介绍什么是 Redux 前,我想先来介绍一下为什么我们要用到 Redux。这就不得不从 React 的本质上面说起。
1.1 React组件树的本质限制
在没有 Redux 之前,React 组件之间传递数据就像下面这样:
But,问题来了,为什么数据不能从 组件A 直接 传到 组件D,为什么中间要经过中转站呢?
注意,此处,React 基础非常好的同学可自动跳过 第一节 非常重要的引言(狗头)
1.1.1 React的数据流规则
React有一个 单向数据流 的核心原则:
数据只能从 父组件 传递给 子组件
兄弟组件之间 不能直接通信
子组件 不能直接 向父组件传递数据(除了通过回调函数)
1.1.2 组件树结构示例
假设我们有这样的组件树结构:
App (根组件)
├── Header
│ └── UserInfo (组件A - 有用户数据)
├── MainContent
│ ├── Sidebar (组件B - 中转站)
│ └── ProductList
│ └── ProductCard (组件D - 需要用户数据)
└── Footer
1.2 为什么不能直接从A传给D?
1.2.1 组件树层级关系
// 组件A和组件D在不同的分支上
App
├── Header
│ └── UserInfo (A) ❌ 不能直接到达 ProductCard (D)
└── MainContent└── ProductList└── ProductCard (D)
A和D不是直接的父子关系,React的props只能沿着父→子的路径传递
1.2.2 具体代码演示
// 这样是不可能的 - A不能直接传给D
function UserInfo() { // 组件Aconst userData = { name: "张三", id: 123 };// 无法直接传递给ProductCard组件!// <ProductCard user={userData} /> // 这里访问不到ProductCard
}function ProductCard() { // 组件D// 无法直接接收UserInfo的数据// const userData = ???; // 从哪里获取?
}
1.2.3 React的设计哲学
React被设计为:
组件封装:每个组件只知道自己的直接子组件
单向数据流:数据向下流动,事件向上冒泡
可预测性:数据流向清晰,便于调试
二、为什么需要 Redux ?
2.1 什么是 Redux?
Redux 是一个用于 JavaScript 应用程序的状态管理库。想象一下,如果你的应用是一个大型商场,Redux 就像是商场的中央控制室,负责管理整个商场的所有信息(比如库存、顾客信息、营业状态等)。
在没有 Redux 之前,React 组件之间传递数据就像下面这样:
有了 Redux 之后,就像这样:
2.2 Redux 的核心概念
Redux 有三个核心概念,我们用一个简单的比喻来理解:
概念 | 比喻 | 作用 | 特点 |
---|---|---|---|
Store | 银行金库 | 存储所有数据 | 唯一、只读 |
Action | 存取款单 | 描述要做什么 | 纯对象、有type |
Reducer | 银行柜员 | 处理业务逻辑 | 纯函数、不可变 |
2.2.1 Store(仓库)
Store 就像一个大仓库,存放着应用的所有状态数据
// 创建一个简单的store
const store = {count: 0,user: { name: '张三', age: 25 }
}
2.2.2 Action(动作)
Action 就像一张说明书,告诉 Redux "我想要做什么"。
// 增加计数的action
const incrementAction = {type: 'INCREMENT' // 必须有type属性
}// 设置用户名的action
const setNameAction = {type: 'SET_NAME',payload: '李四' // 携带数据
}
2.2.3 Reducer(处理器)
Reducer 是一个函数,接收当前状态和 action,返回新的状态。
function counterReducer(state = { count: 0 }, action) {switch (action.type) {case 'INCREMENT':return { count: state.count + 1 }case 'DECREMENT':return { count: state.count - 1 }default:return state}
}
2.3 Redux 的工作流程
2.3.1 工作流程图
2.3.2 简单的计数器例子
// 1. 定义初始状态
const initialState = {count: 0
}// 2. 创建 Reducer
function counterReducer(state = initialState, action) {switch (action.type) {case 'INCREMENT':return { count: state.count + 1 }case 'DECREMENT':return { count: state.count - 1 }default:return state}
}// 3. 创建 Store
const store = Redux.createStore(counterReducer)// 4. 订阅状态变化
store.subscribe(() => {console.log('当前计数:', store.getState().count)
})// 5. 发送 Action
store.dispatch({ type: 'INCREMENT' }) // 计数变为 1
store.dispatch({ type: 'INCREMENT' }) // 计数变为 2
store.dispatch({ type: 'DECREMENT' }) // 计数变为 1
2.4 Redux 三大原则
原则 | 说明 | 好处 | 违反后果 |
---|---|---|---|
单一数据源 | 整个应用只有一个 Store | 状态集中管理,易于调试 | 数据分散,难以追踪 |
状态只读 | 不能直接修改状态 | 保证数据流向可预测 | 状态混乱,难以回溯 |
纯函数修改 | 只能通过 Reducer 修改状态 | 副作用可控,易于测试 | 逻辑复杂,难以维护 |
2.5 Redux vs 其他状态管理方案
特性 | 传统React状态 | Redux状态管理 |
---|---|---|
数据传递 | Props层层传递 | 组件直接访问Store |
组件耦合 | 高度耦合,中间组件被污染 | 低耦合,组件独立 |
状态集中 | 分散在各个组件 | 集中在单一Store |
调试难度 | 困难,难以追踪变化 | 简单,完整的操作历史 |
代码复杂度 | 随组件层级增加而增加 | 相对稳定 |
性能优化 | 需要手动优化 | 自动优化,精确更新 |
团队协作 | 容易产生冲突 | 标准化流程,易协作 |
测试难度 | 需要模拟大量props | 独立测试Action和Reducer |
2.6 Redux 的优缺点总结
优点
可预测性:状态变化完全可预测,便于调试
集中管理:所有状态集中在一个地方
时间旅行:可以回溯到任何历史状态
强大的开发工具:Redux DevTools 提供强大的调试功能
中间件支持:可以轻松添加日志、异步处理等功能
缺点
学习曲线陡峭:概念较多,初学者需要时间理解
代码量大:需要写很多样板代码
过度工程:对于简单应用可能过于复杂
性能开销:每次状态变化都会触发重新渲染
什么时候使用 Redux?