1. Redux 介绍
目标:能够说出什么是 Redux
知识点:
Redux 是 JavaScript 状态容器,提供可预测化的状态管理,是 React 中最常用的状态管理工具,但是 Redux 和 React 之间没有关系。Redux 支持 React、Angular、jQuery 甚至纯 JavaScript。
- React 的问题:
- React 只是 DOM 的一个抽象层(UI 库),并不是 Web 应用的完整解决方案
- 因此 React 在涉及到数据的处理以及组件之间的通信时会比较复杂
- 对于大型的复杂应用来说,这两方面恰恰是最关键的,需要一个专门的状态工具
- Redux 背景介绍:
- 2014 年 Facebook 提出了 Flux 架构的概念(前端状态管理的概念),引发了很多的实现
- 2015 年,Redux 出现,将 Flux 与函数式编程 结合一起,很短时间内就成为了最热门的前端架构
- Flux 是最早的前端的状态管理工具,它提供了状态管理的思想,也提供对应的实现
- 除了 Flux、Redux 之外,还有:Mobx 等状态管理工具
总结:
Redux 是 JavaScript 状态容器,提供可预测化的状态管理,是 React 中最常用的状态管理工具
2. 为什么需要 Redux
目标:能够说出为什么需要使用 Redux
知识点:
- 不使用 redux ( 如左图 )
- 父往子使用 props 传值,子往父通过回调函数
- 兄弟组件通过状态提升,跨组件通信通过 context
- 处理远房亲戚(非父子)关系的组件通讯时乏力
- 组件之间的数据流混乱,出现 Bug 时难定位
- 使用 Redux ( 如右图 ):
- 集中式存储和管理应用的状态
- 处理组件通讯问题时,无视组件之间的层级关系
- 简化大型复杂应用中组件之间的通讯问题
- 数据流清晰,易于定位 Bug
总结:
redux 集中式存储和管理应用的状态,无视组件之间的层级关系,使得数据流清晰
3. Redux 核心概念-概述
目标:知道 Redux 的三个核心概念的职责
知识点:
- 知道三个核心概念是什么
- 为了让代码各部分职责清晰、明确,Redux 代码被分为三个核心概念:action/reducer/store
- 三个核心概念的职责分别是什么
- action -> reducer -> store
- action (动作) :表示页面执行的事件、描述要做的事情 (加、减、乘、法)
- reducer (函数):负责进行状态的更新
- store (仓库):负责数据的存储、数据的变更,整合 action 和 reducer
- Redux 数据交互流程
- 在 Store 中创建数据,网页(视图)使用 Store 中数据进行页面的渲染
- 视图想修改数据,需要通过事件触发 Action;Action 描述做了什么事,执行的事件
- Store 接收到分发的 Action 进行通过 reducer 进行数据的变更或其他处理
- Store 的数据发生变更以后,驱动视图更新。
总结:
- Redux 中的三个核心概念:action -> reducer -> store
4. Redux 核心概念-action
目标:能够定义一个最基本的 action
知识点:
- action 是什么
- action 表示行为、动作,描述要做的事情,项目中的每一个功能都是一个 action
- 例如:
- 计数器案例:计数器加 1、减1
- 购物车案例:获取购物车数据、切换商品选中状态
- 项目:登录,退出等
- action 的特点
- action 只描述做什么事情
- action 是一个 JS 对象,必须带有 type 属性,用于区分动作的类型
- 根据功能的不同,可以携带额外的数据(比如,payload 有效载荷),配合该动作来完成相应功能
落地代码:
// 计数器案例
{ type: 'increment' } // +1
{ type: 'decrement' } // -1
// 累加10操作
{ type: 'increment', payload: 10 } // +10
{ type: 'decrement', payload: 10 } // -10
// 购物车案例
{ type: 'getGoodsList' }
{ type: 'changeGoodsState', payload: { id: 1, goodsState: true } }
// 思考 ??
// 添加任务,action 怎么写?
// 删除任务,action 怎么写?
总结:
action 就是一个对象,type 描述行为,约定 payload 做为传参
5. Redux 核心概念-action creator
目标:掌握来使用函数创建 action
知识点:
- action creator 概念:只使用函数创建 action 对象
- action creator 有点:简化多次使用 action 时,重复创建 action 对象
落地代码:
// 不使用 Action Creator
// 创建多个 action 时,需要重复手动创建 action 对象,很繁琐
{ type: 'decrement', payload: 2 }
{ type: 'decrement', payload: 8 }
// 使用 Action Creator
// 1 先创建 Action Creator 函数
const decrement = payload => {
return { type: 'decrement', payload }
}
// 简化:
const decrement = payload => ({ type: 'decrement', payload })
// 2 创建多个 action,只需要调用 Action Creator 函数即可
decrement(2) // => { type: 'decrement', payload: 2 }
decrement(8) // => { type: 'decrement', payload: 8 }
// 思考 ??
// 删除任务,不使用 Action Creator
// 删除任务,使用 Action Creator
总结:
- 使用 action creator 方式可以动态创建 action
- 使用函数来创建 action 可以动态传参
6. Redux 核心概念-reducer
目标:能够掌握 reducer 的基本写法
知识点:
- reducer 是什么
- 用来处理 action 并更新状态,是 Redux 状态更新 的地方
- 函数签名为:(prevState, action) => newState
- 接收 上一次的状态 和 action 作为参数,根据 action 的类型,执行不同操作,最终返回新的状态
- reducer 使用原则:
- reducer 一定要有返回值,即使状态没有改变也要返回上一次的状态
- reducer 是一个纯函数,不要使用 Math.random() / new Date() / Date.now() / ajax 请求等不纯的操作
- reducer 不能执行JS副作用,不要直接修改当前状态,而是根据当前状态值创建新的状态值(新替旧)
- reducer 语法 ```jsx // 伪代码: // prevState 上一次的状态 // action 当前要执行的动作
const reducer = (prevState, action) => { return newState }
**落地代码:**
```jsx
// 示例:
// state 上一次的状态
// action 当前要执行的动作
const reducer = (state, action) => {
switch (action.type) {
// 计数器增加
case 'increment':
// 返回新状态
// return state + 1
// 根据 action 中提供的 payload 来决定到底增加多少
return state + action.payload
// 注意:一定要有 default,如果将来 reducer 无法处理某个 action,就直接将上一次的状态返回即可
default:
return state
}
}
// 模拟调用
reducer(0, { type: 'increment' }) // 本次执行完成后,状态变为:1
reducer(1, { type: 'increment' }) // 本次执行完成后,状态变为:2
reducer(1, { type: 'decrement' }) // 无法处理该 action,所以返回上一次状态:1
总结:
- reducer 是修改状态的地方,这里根据action的类型去修改状态
- 修改的原则:新值替换旧值,不能发请求和随机返回,不能操作全局变量
7. 纯函数
目标:了解什么是纯函数
知识点:
纯函数是函数式编程中的概念
- 纯函数就是指:一个函数的返回结果只依赖于它的参数或内部的数据,并且在执行过程里面没有副作用
- 如果函数的调用参数相同,则永远返回相同的结果,即相同的输入,总是得到相同的输出
- 返回的结果只依赖于其输入参数和内部变量,不依赖于程序执行期间函数外部任何状态或数据的变化
- 对于纯函数来说,不要执行不纯的操作
- 不能更改传入参数和函数外部的变量
- 不能使用 Date.now()、Math.random()、异步请求等操作,因为每次会得到不一样的结果
参考资料:函数式编程初探
落地代码:
// 纯函数:
const add = () => {
return 123
}
add() // 123
add() // 123
const add = (num1, num2) => {
return num1 + num2
}
add(1, 2) // 3
add(1, 2) // 3
const add = (obj) => {
return obj
}
add({ name: 'jack' }) // { name: 'jack' }
add({ name: 'jack' }) // { name: 'jack' }
// 不是纯函数:
const add = () => {
return Math.random()
}
add() // 0.12311293827497123
add() // 0.82239841238741814
总结:
纯函数:相同的输入,总是得到相同的输出
8. 副作用
目标:了解什么是副作用
知识点:
- 如果一个函数或其他操作修改了其局部环境之外的状态变量值,那么它就被称为有副作用
- 常见的副作用操作:数据获取,设置订阅以及手动更改 React 组件中的 DOM 都属于副作用
落地代码:
// 无副作用
const add = (num1, num2) => {
return num1 + num2
}
add(1, 3)
// ---
// 有副作用:
let c = 0
const add = (num1, num2) => {
// 函数外部的环境产生了影响,所以是有副作用的
c = 1
return num1 + num2
}
add(1, 3)
// 有副作用
const add = (obj) => {
// 因为直接修改了参数的值,对外部的数据产生了影响
obj.num = 123
return obj
}
const o = {}
add(o)
console.log(o) // { num: 123 }
// 有副作用
const add = (num1, num2) => {
// 因为该操作,会让浏览器控制额外的打印一些内容
console.log('add')
return num1 + num2
}
add(1, 3)
总结:
一个函数被称为有副作用,通常因为他们更改了外部的一些变量或状态
9. Redux 核心概念-store
目标:能够通过 store 关联 action 和 reducer
知识点:
store:负责数据的存储、数据的变更,用以整合 action 和 reducer
特点:
- 一个应用只有一个 store
- 创建 store 时接收 reducer 作为参数:const store = createStore(reducer)
- 维护应用的状态,获取状态:store.getState()
- 发起状态更新时,需要分发 action:store.dispatch(action)
- 其他 API:
- 订阅(监听)状态变化:const unSubscribe = store.subscribe(() => {})
- 取消订阅状态变化: unSubscribe()
落地代码:
import ReactDOM from 'react-dom'
import { createStore } from 'redux'
// 给 state 设置默认值
const reducer = (state = 0, action) => {
switch (action.type) {
// 计数器增加
case 'increment':
// 返回新状态
// return state + 1
// 根据 action 中提供的 payload 来决定到底增加多少
return state + action.payload
default:
// 注意:一定要有 default,如果将来 reducer 无法处理某个 action,就直接将上一次的状态返回即可
return state
}
}
// 创建 store
// 参数为:reducer 函数
const store = createStore(reducer)
const increment = (payload) => ({ type: 'increment', payload })
// 更新状态
// dispatch 派遣,派出。表示:分发一个 action,也就是发起状态更新
store.dispatch(increment(2))
// 获取状态
// const state = store.getState()
// console.log(state)
store.subscribe(() => {
// 状态改变时,执行相应操作
// 比如,记录 redux 状态
console.log(store.getState())
})
store.dispatch(increment(2))
// --------------------------
// 取消订阅状态变化,注意:取消订阅只是不执行回调函数
const unSubscribe = store.subscribe(() => {
console.log('我不会被执行')
console.log(store.getState())
})
// 取消监听状态变化
unSubscribe()
store.dispatch(increment(2))
总结:
- store:负责数据的存储、数据的变更,用以整合 action 和 reducer
- 一个应用只有一个 store
- 创建 store:const store = createStore(reducer)
- 获取状态:store.getState()
- 分发 action:store.dispatch(action)
10. Redux 获取状态默认值的执行过程
目标:能够知道 redux 状态默认值的生成
知识点:
只要创建 store,那么,Redux 就会调用一次 reducer,这一次就是初始化默认值
第一次调用 reducer,返回 reducer(undefined, {type: “@@redux/INITv.a.4.t.t.p”})
因为传入的状态值是 undefined ,并且是一个随机的 action type,所以:
- 状态值因为 undefined,所以,我们设置的默认值就会生效 state = 10
- 因为是一个随机的 action type,那就一定会走 default,返回默认值 10
- Redux 内部拿到状态值,就用这个状态值,来作为了 store 中状态的默认值
- 因此,将来当我们调用 store.getState() 方法来获取 Redux 状态值就是默认值
11. Redux 执行过程分析
目标:能够说出 redux 代码的执行流程
知识点:
- 创建 store 时,Redux 就会先调用一次 reducer,来获取到默认状态
- 分发动作 store.dispatch(action) 更新状态
- Redux store 调用 reducer 传入:上一次的状态(当前示例中就是:10)和 action({ type: ‘increment’ }),计算出新的状态并返回
- reducer 执行完毕后,将最新的状态交给 store,store 用最新的状态替换旧状态,状态更新完毕
落地代码:
import { createStore } from 'redux'
const store = createStore(reducer)
// reducer(10, { type: 'increment' })
function reducer(state = 10, action) {
console.log('reducer:', state, action)
switch (action.type) {
case 'increment':
return state + 1
default:
return state
}
}
console.log('状态值为:', store.getState()) // 10
// 发起更新状态:
// 参数: action 对象
store.dispatch({ type: 'increment' })
// 相当于: reducer(10, { type: 'increment' })
console.log('更新后:', store.getState()) // 11