最近入坑React-Native,买了些参考书,撸了一个玩Android的react-native版本,对于参考书里面关于flux和redux是一脸的懵逼,后面在豆瓣上搜了redux相关的书籍,发现一本不错的入坑书深入浅出React和Redux
,里面讲解了原生prop和state在使用过程中的缺点,以及如何一步步演化到redux框架,这里作一下总结。涉及到的知识点:
1. 什么是prop和state
2. 传统prop和state在使用过程中的不足
3. flux
4. redux
prop和state
我们需要先从React组件的数据prop和state开始介绍,这两者无论哪一个发生变化,都可能引起组件的重新渲染,那么它们有什么区别的?prop是组件的对外接口,state是组件的内部状态,对外用prop,内部用state。简单的理解就是React的首要思想是通过组件
来开发应用,state负责管理组件内部的状态,prop负责接收外部传递过来的数据(包含函数等)。
我们以如下的一个简单自定义组件为例:
左侧为文本,右侧为两个按钮,分别实现+、-功能,我们现在通过+、-来修改计数值,左侧文本同步更新,此时我们通过按钮修改计数值,这个组件维护的计数值就属于组件内部管理的状态,即state。那么prop在哪里呢?上面说了prop是组件的对外接口,简单的理解就是外部组件可以通过它向组件内部传递数据(包含回调函数),我们这里就以计数组件的初始值为例,我们允许外部组件通过prop向组件内部传递初始值。
因为我本身是做Android的,所以我的理解就是state相当于一个对象内部的变量,表示着当前对象内部的状态,而prop相当于一个构造,可以通过构造为对象传递初始数据等。
传统prop和state在使用过程中的不足
还是以上面的组件为例,不过现在不是一个,而是有多个,我们现在要统计多个计数组件的总计数。UI大致如下:
场景是这样的,现在下面的总计数需要统计上面三个计数,这就涉及到子组件向父组件传递数据,同时父组件要维护一个状态,这里就涉及到一个问题数据重复,数据重复的话就会出现一个问题:如果重复的数据不一致,那么要以谁为准?
这就是传统prop和state的不足,既然出现了问题,那么肯定就会有人想着去解决,这就是下面要介绍的flux。
Flux
针对上面提到的数据管理方面存在的缺陷,Facebook开源了一个框架Flux,它的思想就是单向数据流。在说单向数据流之前,我们先说一下MVC,在我们的想象当中(理想化)的MVC应该如下图:
因为MVC框架并没有一种阻止Model和View进行直接通信的机制,所以在实际的工作中出于一种方便的想法,经常会出现Mode和View直接通信,所以往往是这样的:
可以发现在Model和View中出现了直接的通信,这就导致模块之间耦合增强,在后期维护中往往出现牵一发而动全身的状况(一改就是bug,完全的懵逼)。出于这种实际的状况,Facebook提出了单向数据流的概念:
对比这张图和上面的MVC框架图,我们很明显的可以发现有以下几个区别:
(1)Flux易于扩展,新增功能只需要添加新的Action类型即可,不用修改原来的代码逻辑
(2)整个数据都是在单一方向上流转,不存在不同的角色间互相通信(单向数据流)
在继续分析Flux之前,我们先弄清楚一下上面架构图中各角色的作用:
(1)Dispatcher:派发Action,全局唯一
(2)Action:一个普通的JavaScipt对象,代表一个动作的纯数据。Action对象必须有一个type字段,代表这个Action对象的类型,建议使用字符串类型
(3)Store:存储应用状态,同时接受Dispatcher派发的动作,根据动作来决定是否要更新应用状态。当Store的状态发生变化时,View也要作出相应的变化。注意所有的Store对象都需要注册到Dispatcher中
(4)View:UI库。
- View如果要改变Store的状态,必须而且只能通过派发Action来实现
- 创建View时,要读取Store上的状态来初始化组件的内部状态(即数据来源不再是prop,而是Store)
- 当Store上状态发生变化时,组件要立刻同步内部状态
根据上面的角色介绍,我们发现Flux有以下的优缺点:
优点:
(1)数据统一管理。原生的React组件,每个组件都要维护驱动自己渲染的状态数据,特别是在多个组件之间的状态有关联时,一锅粥的节奏;通过Flux来获取,每个组件的状态都统一由相应的Store来处理,组件只负责渲染界面,就不存在数据重复的问题了
(2)单向数据流。只能通过派发Action来改变State
缺点:
(1)Store之间可能存在依赖、耦合关系
(2)Store混杂了逻辑和状态,为热加载添加了难度(比如需要根据用户属性来动态加载不同的模块,现在由于Store混杂了逻辑和状态,那么我们就很难在保存状态的情况下,直接替换逻辑)
由于Flux存在这样的缺点,Redux应运而生。
Redux
Redux在Flux单向数据流的技术上,强调了三个基本原则:
(1)唯一数据源。应用的状态数据应该只存储在唯一的一个Store上,这个Store是一个树形的对象,每个组件往往只是对应树形对象上的一部分数据
(2)保持状态只读。这一点和Flux一样,即只能通过派发Action对象来修改Store的状态
(3)数据改变只能通过纯函数。就是说函数的返回结果必须完全由参数来决定,而不产生任何副作用,也不能修改参数
结合上图,我们来了解一下Redux的优化点:
由于唯一数据源(Store唯一,一种树形对象,每个组件的状态只是树形对象中的一小部分),所以不用我们自己来实现Dispatcher,因为Dispatcher的作用就是用来分发Store的,现在既然Store只有一个,就不用我们来实现Dispatcher了,由框架内部实现,要分发唯一Store时,直接调用其dispatch方法即可。
数据改变只能通过纯函数,这里所说的纯函数就是Reducer。Reducer是计算机科学中的通用概念,以JavaScript为例,数组类型就有reduce函数,接收的参数就是reducer,reduce做的事情就是把数组中所有元素依次做“规约”,即对每个元素都调用一次参数reducer函数。
第一点还是比较好理解的,至于第二点可能会觉得有点懵逼,我们这里再说明一下reducer函数吧。在Redux中,每个reducer的函数签名如下:
reducer(state,action)
第一个参数state是当前的状态,第二个参数action是接收到的action对象,reducer要做的事情就是根据state和action的值产生一个新的对象返回。下面是一个简单的reducer函数实现:
//此处state为当前状态,action为接收到的Action对象
function reducer(state,action) => {
//从Action中结构出参数paramX
const {paramX} = action;
//...为扩展操作符,此处表示把state中所有字段扩展开,可以理解为把state对象的所有参数都取出赋值给这个新的对象,
//,后面的[paramX]:函数,表示为这个新对象中的paramX状态赋新值
return {...state,[paramX]:(state[paramX]) => {
//可以针对状态中的对应的值做处理,然后返回新值给paramX
doSomething...
return xxx
}};
}
简单来说这个reducer的作用就是更新state状态。
好了,redux已经在一定程度上解决了Flux框架所遗留的一些不足,不过Redux本身又出现了新的问题:那就是虽然Store是全局唯一,但是我在需要的地方需要去重复导入,就算不重复导入,由父组件导入然后传递给子组件,可是我父组件又用不到,这样也不是很合理。所以一个应用中,最好只有一个地方需要直接导入Store(应用入口),基于这个需求,我们需要来了解一下React的Context功能。React的Context(上下文环境)的作用如下:
这个图还是比较容易理解的,根组件提供一个Context,子组件在需要的时候直接就能去读取这个Context,而不用通过父组件一层层的传递过来,这样就解决了上面说的问题。
我们发现这些都是由现成套路的,我们想要自己实现也可以,如果不想,那么就是用react-redux吧。
由于本人也是刚刚入坑不就,如有不足之处还请指出!!!O(∩_∩)O