Redux是一种数据架构模式,它可以用来管理应用的状态。

之前一直在做Angular的项目,没有使用到过Redux,对于Redux的使用场景和原理都不是很清楚,看资料时作者自己实现了一个Redux,在这里记录一下,加深对Redux原理的理解。

一、基本原理

首先,我们要明白的是:

  • 状态的改变一定是有原因的,这个原因的表现叫作action。
  • 有了action之后,状态不可能自己就改变,需要我们去操作它,这个操作就是reducer函数。
  • 操作状态需要2个条件,之前的状态和触发操作的action,这个操作是为了改变状态,使以这个操作返回一个新的状态。

按照上面的说明,我们先定义一下action和reducer的接口。

 1 // 首先,状态改变一定是由action引起的,至于这个action是由什么触发的(用户点击button、异步通信)我们不关心
 2 export interface Action{
 3     // 在action中只有type是必须的,让我们知道它是哪一类action(是用户点击button的action而不是异步通信的action)
 4     type: string;
 5     // 这个action可能需要带一些数据
 6     payload?: any;
 7 }
 8 // 触发action意味着状态要改变,那我们怎么修改状态呢?
 9 // 从最基本的来说,修改状态我们需要知道两样东西: 以前的状态和触发的action
10 // 那么Reducer函数有2个参数:以前的状态action和触发的action,它还要返回一个新的状态
11 export interface Reducer<T> {
12     (state: T, action: Action): T;
13 }

根据接口我们来使用一下:

 1 let reducer: Reducer<number> = (state: number, action: Action): number => {
 2     // 使用switch来区分是什么action
 3     switch(action.type) {
 4         case 'ADD':
 5             return state + action.payload;
 6         case 'DEC':
 7             return state - action.payload;
 8         default:
 9             // 有一些action我们不想让它改变状态,这里把原来的状态返回回去
10             return state;
11     }
12 }
13 let addAction: Action = {
14     type: 'ADD',
15     payload: 5
16 }
17 let decAction: Action = {
18     type: 'DEC',
19     payload: 2
20 }

可以看到,我们定义了一个怎么处理action使得状态改变的函数。还定义了一个增加的action和减少的action。

下面我们来使用一下:

let state = 10;
console.log(reducer(state, addAction)) // 15
console.log(reducer(state, decAction)) // 8

这里我们先触发了一个加5的action,这时状态变成了15,

然后我们触发了一个减2的操作,因为我们并没有保存刚才减5的后的状态,所以这时的状态还是10,最后的结果就变成了8,

这显然不是我们需要的结果,我们想把状态保存住,那么状态保存在哪里呢?我们新建一个叫作store的容器(一个对象),它用来保存状态state。

 1 // 这是一个类,用它来生成保存状态的容器,
 2 export class Store<T> {
 3     // 状态是不能让外界直接知道的
 4     private _state: T;
 5     // 生成这个容器时我们需要知道操作状态的方法
 6     constructor(private reducer: Reducer<T>) {}
 7     // 外界从容器中得到当前状态的途径
 8     public getState() {
 9         return this._state;
10     }
11     // 状态是保存在容器中的,如果外界发生了什么事情(action),想要改变状态,这时需要有一个接口让容器知道外界有需要改变状态的事件发生了
12     public dispatch(action: Action) {
13         // 以前的状态 + 触发的action = 新的状态
14         this._state = this.reducer(this._state, action);
15     }
16 }

我们来新建一个容器:

// 我们先新建一个容器
let store: Store<number> = new Store<number>(reducer);

发生action时通过容器的dispatch通知reducer去操作状态

1 store.dispatch(addAction);
2 console.log(store.getState()); // 15
3 store.dispatch(decAction);
4 console.log(store.getState()); // 13

可以看到,这里的状态就被保存住了。

上面的就是我理解的Redux保存状态的最基本的逻辑。

容器保存着状态,外界想要改变和获取状态只能通过容器。

获取状态是容易理解的,外界想要改变状态时,要通知(调用dispatch方法)容器有一个action发生了,容器内部知道发生了action,它会结合以前的状态和action产生新的状态来更新自己保存的状态。

(我觉得从代码实现上是比较简单的,但是想从原理上说清楚我是等级不够啊)

二、优化

1、action生成器

  上面的代码中,没一个action都要我们自己声明的,比如如果想定义加1的action和加2的action,可以这样写:

1 let addAction1: Action = {
2     type: 'ADD',
3     payload: 1
4 }
5 let addAction2: Action = {
6     type: 'ADD',
7     payload: 2
8 }

View Code

这显然不是好的体验,可以定义action生成器,把一类的action使用工厂方法生成 

1 function createAddAction(num: number): Action {
2     return {
3         type: 'ADD',
4         payload: num
5     }
6 }

使用时就可以这样:

store.dispatch(createAddAction(1));
store.dispatch(createAddAction(2));

2、主动发出状态的变化

  我们现在在改变状态后是从容器中拉取状态的,有没有办法是状态改变后容器自动推送出改变呢。

  下面使用回调函数来实现容器的自动推送:

  

 1 // 这是一个类,用它来生成保存状态的容器,
 2 export class Store<T> {
 3     // 状态是不能让外界直接知道的
 4     private _state: T;
 5     private _listenCallback: ListenCallback[] = [];
 6     // 生成这个容器时我们需要知道操作状态的方法
 7     constructor(private reducer: Reducer<T>) {}
 8     // 外界从容器中得到当前状态的途径
 9     public getState() {
10         return this._state;
11     }
12     // 外界添加回调函数,在状态改变时会触发回调,让外界知道状态已经改变
13     public subscribe(listenCallback: ListenCallback) {
14         // 添加回调
15         this._listenCallback.push(listenCallback);
16         // 给外界关闭通知的接口
17         return () => {
18             this._listenCallback = this._listenCallback.filter(
19                 (l: ListenCallback) => l !== listenCallback
20             );
21         }
22     }
23     // 状态是保存在容器中的,如果外界发生了什么事情(action),想要改变状态,这时需要有一个接口让容器知道外界有需要改变状态的事件发生了
24     public dispatch(action: Action) {
25         // 以前的状态 + 触发的action = 新的状态
26         this._state = this.reducer(this._state, action);
27         // 通知外界状态改变了
28         this._listenCallback.forEach(
29             (l: ListenCallback) => l()
30         )
31     }
32 }

注意13行,这里提供了一个接口,让外界可以知道状态改变了,28行是状态改变时通知外界。

可以这样使用:

1 let unsubscribe = store.subscribe(
2     () => console.log(store.getState())
3 );
4 store.dispatch(createAddAction(1));
5
6 store.dispatch(createAddAction(1));
7 unsubscribe();
8 store.dispatch(createAddAction(1));

完整的代码可以在https://github.com/wangzting/miniRedux这里找到。

我在尝试从原理出发而不是从代码出发来解释Redux,我想知道Redux的作者是怎么思考出这个数据架构的,他是怎么把简单的道理用规范的代码表现出来的,显然,我做的不好。

任重而道远,我会继续努力。

作成:2019-02-13

修改:

  2019-02-13 添加码源地址

参考:《Angular权威教程》

转载于:https://www.cnblogs.com/wangtingnoblog/p/Redux.html

Redux入门之实现一个迷你版的Redux相关推荐

  1. 还没理解微前端?手把手教你实现一个迷你版

    大厂技术  高级前端  Node进阶 点击上方 程序员成长指北,关注公众号 回复1,加入高级Node交流群 最近看了几个微前端框架的源码(single-spa[1].qiankun[2].micro- ...

  2. 写一个迷你版Smarty模板引擎,对认识模板引擎原理非常好(附代码)

    前些时间在看创智博客韩顺平的Smarty模板引擎教程,再结合自己跟李炎恢第二季开发中CMS系统写的tpl模板引擎.今天就写一个迷你版的Smarty引擎,虽然说我并没有深入分析过Smarty的源码,但是 ...

  3. ASP.NET Core管道深度剖析(2):创建一个“迷你版”的管道来模拟真实管道请求处理流程

    从<ASP.NET Core管道深度剖析(1):采用管道处理HTTP请求>我们知道ASP.NET Core请求处理管道由一个服务器和一组有序的中间件组成,所以从总体设计来讲是非常简单的,但 ...

  4. python 制作自己的新闻_新闻-十行代码,用Python做一个迷你版的美图秀秀

    十行代码,用Python做一个迷你版的美图秀秀 2020-02-28 10:16:08 作者: 匿名 浏览量:65次 美图秀秀相信大家都不陌生,大家只要操作美图秀秀,就可以P掉图片中脸上的一些瑕疵,让 ...

  5. 如何手写一个迷你版的RPC

    点击上方"Java后端技术栈"关注 持续推送技术干货 前言 在实际后台服务开发中,比如订单服务(开发者A负责)需要调用商品服务(开发者B负责),那么开发者B会和A约定调用API,以 ...

  6. 实现一个迷你版的RPC

    目录 前言 动手实现RPC 商品服务工程 商品对象 商品查询API接口 rpc实现方法 商品API的具体实现 运行结果 前言 在实际后台服务开发中,比如订单服务(开发者A负责)需要调用商品服务(开发者 ...

  7. 手写一个迷你版Spring MVC框架

    前期准备 我这里要写的是一个迷你版的Spring MVC,我将在一个干净的web工程开始开发,不引入Spring,完全通过JDK来实现. 我们先来看一眼工程: 工程代码结构 第一:在annotatio ...

  8. 【手写系列】写一个迷你版的Tomcat

    前言 Tomcat,这只3脚猫,大学的时候就认识了,直到现在工作中,也常会和它打交道.这是一只神奇的猫,今天让我来抽象你,实现你! Tomcat Write MyTomcat Tomcat是非常流行的 ...

  9. 从零开始写一个迷你版的Tomcat

    点击上方"方志朋",选择"设为星标" 回复"666"获取新整理的面试文章 来源 | www.jianshu.com/p/dce1ee01fb ...

最新文章

  1. Javascript s02
  2. python恶搞-python—做一个恶搞程序
  3. 给电脑换源 npm 国内镜像 cnpm
  4. 计算机办公软件技能大赛试题,第七届计算机技能大赛办公软件操作比赛顺利举行...
  5. 自由软件基金会庆祝成立35周年
  6. 阶乘取模算法java_np问题(大数阶乘取模)
  7. idea新建xml文件
  8. outlook邮箱邮件大小限制_outlook上传文件有大小限制如何解决
  9. python log函数_python装饰器的使用
  10. 程序员面试金典——5.7找出缺失的整数
  11. 笔记:css中的position定位
  12. python3 自定义排序_Python3中的自定义排序
  13. swfobject.js 简介
  14. 图神经网络入门:GCN论文+源码超级详细注释讲解!
  15. shadow密码字段为!!_您的密码可能不如您想像的安全!
  16. 【Python】爬虫爬取各大网站新闻(一)
  17. 用Andriod studio学习制作APP
  18. 简单实现一个手持弹幕功能+文字抖动特效
  19. 第一次git拉取代码到本地及身份验证失败踩坑
  20. 使用云主机,我们可以做哪些事情

热门文章

  1. node中npm安装模块的网络问题
  2. Codeforces Beta Round #17 D. Notepad (数论 + 广义欧拉定理降幂)
  3. Spring Boot由jar包转成war包
  4. csu 1756(数论+去重)
  5. Http client 请求
  6. c++面对对象设计 2
  7. 职业生涯中的八大“定位法则”(转)
  8. ASP.NET CORE 入门教程(附源码)
  9. docker系列之file基本操作
  10. 服务器出现 nginx 502 Bad Gateway