理解Redux

理解Redux

Tags
状态管理
React.js
Redux
CreatedTime
Aug 22, 2022 05:26 AM
Slug
2020-12-24-redux
UpdatedTime
Last updated August 22, 2022

为什么需要 redux

就个人来说,偏前,开发过前端组件以及云开发控制台;偏后,使用 nodejs 开发过云开发的数据流中台以云开发网关,日流量达到 10 亿+的级别。
目前微服务理念非常火,后端架构都像无状态服务转变,这方便基于 k8s 的横向扩容,应对突发流量。
但是在前端开发中,尤其是控制台这种业务逻辑很重、交互细节繁多的场景下,都是基于数据状态来渲染视图。在 reactjs/vuejs 中,开发者只需要关注并且维护好数据状态即可。
那么问题来了,重交互的前端,怎么维护好这些细节呢?举个例子,对于一个 ajax 请求,它有 4 种状态:
  • 未发送
  • 发送中
  • 收到结果
    • 成功返回
    • 出错失败
那么根据这 4 种状态,就要展示不同的视图给用户。1 个 ajax 还好,但是现在 pc 端页面动则几十个异步请求,状态的维护变得日渐复杂。除了 ajax 的场景,表单、编辑器等,也是需要维护很多状态。
而 redux 的出现,就解决了前端的状态维护问题

什么是 redux

从上面可以看出来,redux 是状态管理工具。和直接修改状态相比,redux 有那些好处呢?
0、redux 不局限于 react
准确来说,redux 不局限于前端。它可以直接在 node 环境中运行,只是平时常说的 redux 基本都是 react-redux,相较于原生 redux,它在更新状态的时候,多了一步触发 react 视图刷新的操作。
由于现在服务都流行无状态化,后端一般也没有前端这么复杂细致的状态管理,所以后端并不常用 redux。但是为了方便说明,之后的演示都是直接在 nodejs 环境下。
1、redux 中,状态的变动是通过函数调用来触发的
如果直接写原生状态修改,当代码量变多,或者维护者变多的时候,不看代码无法知道哪些状态被修改了,并且只能通过拆解代码来确定状态更改的顺序。
const nestedInfo = { people: { location: { country: "china", }, }, }; // 修改状态 nestedInfo.people.location.country = "zh";
如果通过 redux,本质是通过函数调用来触发修改。所有的修改都要经过 redux,交由它来控制。配合一些 redux 中间件,可以在控制台输出变动过程,整个状态变动不再是黑盒子。
store.dispatch({ type: "CHANGE_PEOPLE", payload: { people: { location: { country: "china", }, }, }, });
2、redux 可以处理异步
redux-think、redux-promise、redux-saga 都属于 redux 的中间件或者基于 redux 的异步解决方案。其中,redux-saga 基于 yield 协程,提供了更多的异步操作符。基于 redux-saga 再次封装的 saga-duck,借助 duck 编程思路,进一步统一了状态编程的写法,也是目前腾讯云控制台的标准写法。
这些中间件是基于 redux 的中间件机制,类似 koajs 的洋葱模型。通过挂入中间件,来实现更多功能。这个之后会详细提到。

基本概念和数据流

redux 由部分组成:
  • Store:全局唯一,是一个大对象,所有的状态都在其上更新
  • Action:用来定义更新状态的动作,是一个对象
  • Reducer:用来更新状态,是一个函数
下面是个简单用例:
/* * @Author: dongyuanxin * @Date: 2020-12-24 23:24:33 * @Github: https://github.com/dongyuanxin/blog * @Blog: https://0x98k.com/ * @Description: redux的简单用例 */ const { createStore } = require("redux"); // 这就是一个Reducer,用来更新状态 function counter(state = 0, action) { // state是上一次状态 // 根据action.type字段,来决定如何进行更新 switch (action.type) { case "INCR": console.log("[1] incr"); return state + 1; case "DECR": console.log("[1] decr"); return state - 1; default: // 一定要有默认返回上次状态,否则状态可能会丢失 return state; } } // 全局唯一store,假设是个数字,初始值是1 // 注意:如果没有初始值,会在创建store的时候自动调用一次reducer,用来初始化状态 const store = createStore(counter, 1); // 通过subscribe监听状态变化,这里直接打印最新状态 store.subscribe(() => { console.log(">>> state is", store.getState()); }); // 调用reducer更新,使其+1 store.dispatch({ type: "INCR" });
整个过程是:通过diapatch函数,将action 下发到 reducer,reducer 根据 action.type 和 action.payload 来更新 store(action => reducer => store)。

参考