Redux入门之实现一个迷你版的Redux
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相关推荐
- 还没理解微前端?手把手教你实现一个迷你版
大厂技术 高级前端 Node进阶 点击上方 程序员成长指北,关注公众号 回复1,加入高级Node交流群 最近看了几个微前端框架的源码(single-spa[1].qiankun[2].micro- ...
- 写一个迷你版Smarty模板引擎,对认识模板引擎原理非常好(附代码)
前些时间在看创智博客韩顺平的Smarty模板引擎教程,再结合自己跟李炎恢第二季开发中CMS系统写的tpl模板引擎.今天就写一个迷你版的Smarty引擎,虽然说我并没有深入分析过Smarty的源码,但是 ...
- ASP.NET Core管道深度剖析(2):创建一个“迷你版”的管道来模拟真实管道请求处理流程
从<ASP.NET Core管道深度剖析(1):采用管道处理HTTP请求>我们知道ASP.NET Core请求处理管道由一个服务器和一组有序的中间件组成,所以从总体设计来讲是非常简单的,但 ...
- python 制作自己的新闻_新闻-十行代码,用Python做一个迷你版的美图秀秀
十行代码,用Python做一个迷你版的美图秀秀 2020-02-28 10:16:08 作者: 匿名 浏览量:65次 美图秀秀相信大家都不陌生,大家只要操作美图秀秀,就可以P掉图片中脸上的一些瑕疵,让 ...
- 如何手写一个迷你版的RPC
点击上方"Java后端技术栈"关注 持续推送技术干货 前言 在实际后台服务开发中,比如订单服务(开发者A负责)需要调用商品服务(开发者B负责),那么开发者B会和A约定调用API,以 ...
- 实现一个迷你版的RPC
目录 前言 动手实现RPC 商品服务工程 商品对象 商品查询API接口 rpc实现方法 商品API的具体实现 运行结果 前言 在实际后台服务开发中,比如订单服务(开发者A负责)需要调用商品服务(开发者 ...
- 手写一个迷你版Spring MVC框架
前期准备 我这里要写的是一个迷你版的Spring MVC,我将在一个干净的web工程开始开发,不引入Spring,完全通过JDK来实现. 我们先来看一眼工程: 工程代码结构 第一:在annotatio ...
- 【手写系列】写一个迷你版的Tomcat
前言 Tomcat,这只3脚猫,大学的时候就认识了,直到现在工作中,也常会和它打交道.这是一只神奇的猫,今天让我来抽象你,实现你! Tomcat Write MyTomcat Tomcat是非常流行的 ...
- 从零开始写一个迷你版的Tomcat
点击上方"方志朋",选择"设为星标" 回复"666"获取新整理的面试文章 来源 | www.jianshu.com/p/dce1ee01fb ...
最新文章
- Javascript s02
- python恶搞-python—做一个恶搞程序
- 给电脑换源 npm 国内镜像 cnpm
- 计算机办公软件技能大赛试题,第七届计算机技能大赛办公软件操作比赛顺利举行...
- 自由软件基金会庆祝成立35周年
- 阶乘取模算法java_np问题(大数阶乘取模)
- idea新建xml文件
- outlook邮箱邮件大小限制_outlook上传文件有大小限制如何解决
- python log函数_python装饰器的使用
- 程序员面试金典——5.7找出缺失的整数
- 笔记:css中的position定位
- python3 自定义排序_Python3中的自定义排序
- swfobject.js 简介
- 图神经网络入门:GCN论文+源码超级详细注释讲解!
- shadow密码字段为!!_您的密码可能不如您想像的安全!
- 【Python】爬虫爬取各大网站新闻(一)
- 用Andriod studio学习制作APP
- 简单实现一个手持弹幕功能+文字抖动特效
- 第一次git拉取代码到本地及身份验证失败踩坑
- 使用云主机,我们可以做哪些事情