image.png

1. Redux 介绍

目标:能够说出什么是 Redux

知识点:

Redux 是 JavaScript 状态容器,提供可预测化的状态管理,是 React 中最常用的状态管理工具,但是 Redux 和 React 之间没有关系。Redux 支持 React、Angular、jQuery 甚至纯 JavaScript。

  1. React 的问题:
  • React 只是 DOM 的一个抽象层(UI 库),并不是 Web 应用的完整解决方案
  • 因此 React 在涉及到数据的处理以及组件之间的通信时会比较复杂
  • 对于大型的复杂应用来说,这两方面恰恰是最关键的,需要一个专门的状态工具
  1. Redux 背景介绍:
  • 2014 年 Facebook 提出了 Flux 架构的概念(前端状态管理的概念),引发了很多的实现
  • 2015 年,Redux 出现,将 Flux 与函数式编程 结合一起,很短时间内就成为了最热门的前端架构
  • Flux 是最早的前端的状态管理工具,它提供了状态管理的思想,也提供对应的实现
  • 除了 Flux、Redux 之外,还有:Mobx 等状态管理工具

总结:

Redux 是 JavaScript 状态容器,提供可预测化的状态管理,是 React 中最常用的状态管理工具

2. 为什么需要 Redux

目标:能够说出为什么需要使用 Redux

image.png

知识点:

  1. 不使用 redux ( 如左图 )
  • 父往子使用 props 传值,子往父通过回调函数
  • 兄弟组件通过状态提升,跨组件通信通过 context
  • 处理远房亲戚(非父子)关系的组件通讯时乏力
  • 组件之间的数据流混乱,出现 Bug 时难定位
  1. 使用 Redux ( 如右图 ):
  • 集中式存储和管理应用的状态
  • 处理组件通讯问题时,无视组件之间的层级关系
  • 简化大型复杂应用中组件之间的通讯问题
  • 数据流清晰,易于定位 Bug

总结:

redux 集中式存储和管理应用的状态,无视组件之间的层级关系,使得数据流清晰

3. Redux 核心概念-概述

目标:知道 Redux 的三个核心概念的职责

知识点:

image.png

  1. 知道三个核心概念是什么
  • 为了让代码各部分职责清晰、明确,Redux 代码被分为三个核心概念:action/reducer/store
  1. 三个核心概念的职责分别是什么
  • action -> reducer -> store
  • action (动作) :表示页面执行的事件、描述要做的事情 (加、减、乘、法)
  • reducer (函数):负责进行状态的更新
  • store (仓库):负责数据的存储、数据的变更,整合 action 和 reducer
  1. Redux 数据交互流程
  • 在 Store 中创建数据,网页(视图)使用 Store 中数据进行页面的渲染
  • 视图想修改数据,需要通过事件触发 Action;Action 描述做了什么事,执行的事件
  • Store 接收到分发的 Action 进行通过 reducer 进行数据的变更或其他处理
  • Store 的数据发生变更以后,驱动视图更新。

总结:

  • Redux 中的三个核心概念:action -> reducer -> store

4. Redux 核心概念-action

目标:能够定义一个最基本的 action

知识点:

  1. action 是什么
  • action 表示行为、动作,描述要做的事情,项目中的每一个功能都是一个 action
  • 例如:
    • 计数器案例:计数器加 1、减1
    • 购物车案例:获取购物车数据、切换商品选中状态
    • 项目:登录,退出等
  1. action 的特点
  • action 只描述做什么事情
  • action 是一个 JS 对象,必须带有 type 属性,用于区分动作的类型
  • 根据功能的不同,可以携带额外的数据(比如,payload 有效载荷),配合该动作来完成相应功能

落地代码:

  1. // 计数器案例
  2. { type 'increment' } // +1
  3. { type 'decrement' } // -1
  4. // 累加10操作
  5. { type 'increment', payload: 10 } // +10
  6. { type 'decrement', payload: 10 } // -10
  7. // 购物车案例
  8. { type: 'getGoodsList' }
  9. { type: 'changeGoodsState', payload: { id: 1, goodsState: true } }
  10. // 思考 ??
  11. // 添加任务,action 怎么写?
  12. // 删除任务,action 怎么写?

总结:

action 就是一个对象,type 描述行为,约定 payload 做为传参

5. Redux 核心概念-action creator

目标:掌握来使用函数创建 action

知识点:

  1. action creator 概念:只使用函数创建 action 对象
  2. action creator 有点:简化多次使用 action 时,重复创建 action 对象

落地代码:

  1. // 不使用 Action Creator
  2. // 创建多个 action 时,需要重复手动创建 action 对象,很繁琐
  3. { type: 'decrement', payload: 2 }
  4. { type: 'decrement', payload: 8 }
  5. // 使用 Action Creator
  6. // 1 先创建 Action Creator 函数
  7. const decrement = payload => {
  8. return { type: 'decrement', payload }
  9. }
  10. // 简化:
  11. const decrement = payload => ({ type: 'decrement', payload })
  12. // 2 创建多个 action,只需要调用 Action Creator 函数即可
  13. decrement(2) // => { type: 'decrement', payload: 2 }
  14. decrement(8) // => { type: 'decrement', payload: 8 }
  15. // 思考 ??
  16. // 删除任务,不使用 Action Creator
  17. // 删除任务,使用 Action Creator

总结:

  • 使用 action creator 方式可以动态创建 action
  • 使用函数来创建 action 可以动态传参

6. Redux 核心概念-reducer

目标:能够掌握 reducer 的基本写法

知识点:

  1. reducer 是什么
  • 用来处理 action 并更新状态,是 Redux 状态更新 的地方
  • 函数签名为:(prevState, action) => newState
  • 接收 上一次的状态 和 action 作为参数,根据 action 的类型,执行不同操作,最终返回新的状态
  1. reducer 使用原则:
  • reducer 一定要有返回值,即使状态没有改变也要返回上一次的状态
  • reducer 是一个纯函数,不要使用 Math.random() / new Date() / Date.now() / ajax 请求等不纯的操作
  • reducer 不能执行JS副作用,不要直接修改当前状态,而是根据当前状态值创建新的状态值(新替旧)
  1. reducer 语法 ```jsx // 伪代码: // prevState 上一次的状态 // action 当前要执行的动作

const reducer = (prevState, action) => { return newState }

  1. **落地代码:**
  2. ```jsx
  3. // 示例:
  4. // state 上一次的状态
  5. // action 当前要执行的动作
  6. const reducer = (state, action) => {
  7. switch (action.type) {
  8. // 计数器增加
  9. case 'increment':
  10. // 返回新状态
  11. // return state + 1
  12. // 根据 action 中提供的 payload 来决定到底增加多少
  13. return state + action.payload
  14. // 注意:一定要有 default,如果将来 reducer 无法处理某个 action,就直接将上一次的状态返回即可
  15. default:
  16. return state
  17. }
  18. }
  19. // 模拟调用
  20. reducer(0, { type: 'increment' }) // 本次执行完成后,状态变为:1
  21. reducer(1, { type: 'increment' }) // 本次执行完成后,状态变为:2
  22. reducer(1, { type: 'decrement' }) // 无法处理该 action,所以返回上一次状态:1

总结:

  • reducer 是修改状态的地方,这里根据action的类型去修改状态
  • 修改的原则:新值替换旧值,不能发请求和随机返回,不能操作全局变量

7. 纯函数

目标:了解什么是纯函数

知识点:

纯函数是函数式编程中的概念

  1. 纯函数就是指:一个函数的返回结果只依赖于它的参数或内部的数据,并且在执行过程里面没有副作用
  2. 如果函数的调用参数相同,则永远返回相同的结果,即相同的输入,总是得到相同的输出
  3. 返回的结果只依赖于其输入参数和内部变量,不依赖于程序执行期间函数外部任何状态或数据的变化
  4. 对于纯函数来说,不要执行不纯的操作
  • 不能更改传入参数和函数外部的变量
  • 不能使用 Date.now()、Math.random()、异步请求等操作,因为每次会得到不一样的结果

参考资料:函数式编程初探

落地代码:

  1. // 纯函数:
  2. const add = () => {
  3. return 123
  4. }
  5. add() // 123
  6. add() // 123
  7. const add = (num1, num2) => {
  8. return num1 + num2
  9. }
  10. add(1, 2) // 3
  11. add(1, 2) // 3
  12. const add = (obj) => {
  13. return obj
  14. }
  15. add({ name: 'jack' }) // { name: 'jack' }
  16. add({ name: 'jack' }) // { name: 'jack' }
  17. // 不是纯函数:
  18. const add = () => {
  19. return Math.random()
  20. }
  21. add() // 0.12311293827497123
  22. add() // 0.82239841238741814

总结:

纯函数:相同的输入,总是得到相同的输出

8. 副作用

目标:了解什么是副作用

知识点:

  1. 如果一个函数或其他操作修改了其局部环境之外的状态变量值,那么它就被称为有副作用
  2. 常见的副作用操作:数据获取,设置订阅以及手动更改 React 组件中的 DOM 都属于副作用

落地代码:

  1. // 无副作用
  2. const add = (num1, num2) => {
  3. return num1 + num2
  4. }
  5. add(1, 3)
  6. // ---
  7. // 有副作用:
  8. let c = 0
  9. const add = (num1, num2) => {
  10. // 函数外部的环境产生了影响,所以是有副作用的
  11. c = 1
  12. return num1 + num2
  13. }
  14. add(1, 3)
  15. // 有副作用
  16. const add = (obj) => {
  17. // 因为直接修改了参数的值,对外部的数据产生了影响
  18. obj.num = 123
  19. return obj
  20. }
  21. const o = {}
  22. add(o)
  23. console.log(o) // { num: 123 }
  24. // 有副作用
  25. const add = (num1, num2) => {
  26. // 因为该操作,会让浏览器控制额外的打印一些内容
  27. console.log('add')
  28. return num1 + num2
  29. }
  30. add(1, 3)

总结:

一个函数被称为有副作用,通常因为他们更改了外部的一些变量或状态

9. Redux 核心概念-store

目标:能够通过 store 关联 action 和 reducer

知识点:

  1. store:负责数据的存储、数据的变更,用以整合 action 和 reducer

  2. 特点:

  • 一个应用只有一个 store
  • 创建 store 时接收 reducer 作为参数:const store = createStore(reducer)
  • 维护应用的状态,获取状态:store.getState()
  • 发起状态更新时,需要分发 action:store.dispatch(action)
  1. 其他 API:
  • 订阅(监听)状态变化:const unSubscribe = store.subscribe(() => {})
  • 取消订阅状态变化: unSubscribe()

落地代码:

  1. import ReactDOM from 'react-dom'
  2. import { createStore } from 'redux'
  3. // 给 state 设置默认值
  4. const reducer = (state = 0, action) => {
  5. switch (action.type) {
  6. // 计数器增加
  7. case 'increment':
  8. // 返回新状态
  9. // return state + 1
  10. // 根据 action 中提供的 payload 来决定到底增加多少
  11. return state + action.payload
  12. default:
  13. // 注意:一定要有 default,如果将来 reducer 无法处理某个 action,就直接将上一次的状态返回即可
  14. return state
  15. }
  16. }
  17. // 创建 store
  18. // 参数为:reducer 函数
  19. const store = createStore(reducer)
  20. const increment = (payload) => ({ type: 'increment', payload })
  21. // 更新状态
  22. // dispatch 派遣,派出。表示:分发一个 action,也就是发起状态更新
  23. store.dispatch(increment(2))
  24. // 获取状态
  25. // const state = store.getState()
  26. // console.log(state)
  27. store.subscribe(() => {
  28. // 状态改变时,执行相应操作
  29. // 比如,记录 redux 状态
  30. console.log(store.getState())
  31. })
  32. store.dispatch(increment(2))
  33. // --------------------------
  34. // 取消订阅状态变化,注意:取消订阅只是不执行回调函数
  35. const unSubscribe = store.subscribe(() => {
  36. console.log('我不会被执行')
  37. console.log(store.getState())
  38. })
  39. // 取消监听状态变化
  40. unSubscribe()
  41. store.dispatch(increment(2))

总结:

  • store:负责数据的存储、数据的变更,用以整合 action 和 reducer
  • 一个应用只有一个 store
  • 创建 store:const store = createStore(reducer)
  • 获取状态:store.getState()
  • 分发 action:store.dispatch(action)

10. Redux 获取状态默认值的执行过程

目标:能够知道 redux 状态默认值的生成

知识点:

  1. 只要创建 store,那么,Redux 就会调用一次 reducer,这一次就是初始化默认值

  2. 第一次调用 reducer,返回 reducer(undefined, {type: “@@redux/INITv.a.4.t.t.p”})

  3. 因为传入的状态值是 undefined ,并且是一个随机的 action type,所以:

  • 状态值因为 undefined,所以,我们设置的默认值就会生效 state = 10
  • 因为是一个随机的 action type,那就一定会走 default,返回默认值 10
  • Redux 内部拿到状态值,就用这个状态值,来作为了 store 中状态的默认值
  • 因此,将来当我们调用 store.getState() 方法来获取 Redux 状态值就是默认值

11. Redux 执行过程分析

目标:能够说出 redux 代码的执行流程

知识点:

  1. 创建 store 时,Redux 就会先调用一次 reducer,来获取到默认状态
  2. 分发动作 store.dispatch(action) 更新状态
  3. Redux store 调用 reducer 传入:上一次的状态(当前示例中就是:10)和 action({ type: ‘increment’ }),计算出新的状态并返回
  4. reducer 执行完毕后,将最新的状态交给 store,store 用最新的状态替换旧状态,状态更新完毕

落地代码:

  1. import { createStore } from 'redux'
  2. const store = createStore(reducer)
  3. // reducer(10, { type: 'increment' })
  4. function reducer(state = 10, action) {
  5. console.log('reducer:', state, action)
  6. switch (action.type) {
  7. case 'increment':
  8. return state + 1
  9. default:
  10. return state
  11. }
  12. }
  13. console.log('状态值为:', store.getState()) // 10
  14. // 发起更新状态:
  15. // 参数: action 对象
  16. store.dispatch({ type: 'increment' })
  17. // 相当于: reducer(10, { type: 'increment' })
  18. console.log('更新后:', store.getState()) // 11