3.2.1 Redux的基本原则
您可以在百度里搜索“深入浅出React和Redux 艾草文学(www.321553.xyz)”查找最新章节!
3.2.1 Redux的基本原则
2013年问世的Flux饱受争议,而2015年Dan Abramov提出了在Flux基础上的改进框架Redux,则是一鸣惊人,在所有Flux的变体中算是最受关注的框架,没有之一。
Flux的基本原则是“单向数据流”,Redux在此基础上强调三个基本原则:
·唯一数据源(Single Source of Truth);
·保持状态只读(State is read-only);
·数据改变只能通过纯函数完成(Changes are made with pure functions)。
让我们逐一解释这三条基本原则。
1.唯一数据源
唯一数据源指的是应用的状态数据应该只存储在唯一的一个Store上。
我们已经知道,在Flux中,应用可以拥有多个Store,往往根据功能把应用的状态数据划分给若干个Store分别存储管理。比如,在上面的ControlPanel例子中,我们创造了CounterStore和SummaryStore。
如果状态数据分散在多个Store中,容易造成数据冗余,这样数据一致性方面就会出问题。虽然利用Dispatcher的waitFor方法可以保证多个Store之间的更新顺序,但是这又产生了不同Store之间的显示依赖关系,这种依赖关系的存在增加了应用的复杂度,容易带来新的问题。
Redux对这个问题的解决方法就是,整个应用只保持一个Store,所有组件的数据源就是这个Store上的状态。
注意
Redux并没有阻止一个应用拥有多个Store,只是,在Redux的框架下,让一个应用拥有多个Store不会带来任何好处,最后还不如使用一个Store更容易组织代码。
这个唯一Store上的状态,是一个树形的对象,每个组件往往只是用树形对象上一部分的数据,而如何设计Store上状态的结构,就是Redux应用的核心问题,我们接下来会描述具体细节。
2.保持状态只读
保持状态只读,就是说不能去直接修改状态,要修改Store的状态,必须要通过派发一个action对象完成,这一点,和Flux的要求并没有什么区别。
如果只看这个原则的字面意思,可能会让读者感觉有点费解,还记得那个公式吗?UI=render(state),我们已经说过驱动用户界面更改的是状态,如果状态都是只读的不能修改,怎么可能引起用户界面的变化呢?
当然,要驱动用户界面渲染,就要改变应用的状态,但是改变状态的方法不是去修改状态上值,而是创建一个新的状态对象返回给Redux,由Redux完成新的状态的组装。
这就直接引出了下面的第三个基本原则。
3.数据改变只能通过纯函数完成
这里所说的纯函数就是Reducer,Redux这个名字的前三个字母Red代表的就是Reducer。按照创作者Dan Abramov的说法,Redux名字的含义是Reducer+Flux。
Reducer不是一个Redux特定的术语,而是一个计算机科学中的通用概念,很多语言和框架都有对Reducer函数的支持。就以JavaScript为例,数组类型就有reduce函数,接受的参数就是一个reducer,reduce做的事情就是把数组所有元素依次做“规约”,对每个元素都调用一次参数reducer,通过reducer函数完成规约所有元素的功能。
下面是一个使用reducer函数的例子:
[1, 2, 3, 4].reduce(function reducer(accumulation, item) {
return accumulation + item
}, 0);
上面的代码中,reducer(注意不是reduce)函数接受两个参数,第一个参数是上一次规约的结果,第二个参数是这一次规约的元素,函数体是返回两者之和,所以这个规约的结果就是所有元素之和。
在Redux中,每个reducer的函数签名如下所示:
reducer(state, action)
第一个参数state是当前的状态,第二个参数action是接收到的action对象,而reducer函数要做的事情,就是根据state和action的值产生一个新的对象返回,注意reducer必须是纯函数,也就是说函数的返回结果必须完全由参数state和action决定,而且不产生任何副作用,也不能修改参数state和action对象。
让我们回顾一下Flux中的Store是如何处理函数的,代码如下:
CounterStore.dispatchToken = AppDispatcher.register((action) => {
if (action.type === ActionTypes.INCREMENT) {
counterValues[action.counterCaption] ++;
CounterStore.emitChange();
} else if (action.type === ActionTypes.DECREMENT) {
counterValues[action.counterCaption] --;
CounterStore.emitChange();
}
});
Flux更新状态的函数只有一个参数action,因为状态是由Store直接管理的,所以处理函数中会看到代码直接更新state;在Redux中,一个实现同样功能的reducer代码如下:
function reducer(state, action) => {
const {counterCaption} = action;
switch (action.type) {
case ActionTypes.INCREMENT:
return {...state, [counterCaption]: state[counterCaption] + 1};
case ActionTypes.DECREMENT:
return {...state, [counterCaption]: state[counterCaption] - 1};
default:
return state
}
}
可以看到reducer函数不光接受action为参数,还接受state为参数。也就是说,Redux的reducer只负责计算状态,却并不负责存储状态。
我们会在后面的实例中详细解释这个reducer的构造。
读到这里,读者可能会有一个疑问,从Redux的基本原则来看,Redux并没有赋予我们强大的功能,反而是给开发者增加了很多限制啊,开发者丧失了想怎么写就怎么写的灵活度。
“如果你愿意限制做事方式的灵活度,你几乎总会发现可以做得更好。”
——John Carmark
作为制作出《Doom》《Quake》这样游戏的杰出开发者,John Carmark这句话道出了软件开发中的一个真谛。
在计算机编程的世界里,完成任何一件任务,可能都有一百种以上的方法,但是无节制的灵活度反而让软件难以维护,增加限制是提高软件质量的法门。 深入浅出React和Redux