REDUX
1.redux究竟是个什么玩意 ?
如果要谈起redux,我们必须要了解什么叫做 函数式编程 ,其实我们程序的组件开发属于函数式编程的升级版,函数式编程带给我们的优势可以说非常之多,什么功能划分更容易,什么维护更简便,什么高内聚低耦合等等优势在此不再赘述,主要谈一谈函数式编程的缺点。
1. 函数式编程容易编程链式调用的模式。
function foo(){
var a = funA();
var b = funB();
return funC(b);
}
function funA(arg){
return arg + 1;
}
function funB(arg){
return arg + 2;
}
function funC(arg){
return arg + 3;
}
类似于这样的函数在开发之中带给我们的困扰可以说是相当之多了,那么问题来了,我们为什么要用这样的模式编写函数那?
因为函数的作用域是独立的, 所以我们在函数之间进行数据通讯的时候往往需要通过传递参数
或者使用bind
等方法进行参数传递,这样的方式带给我们的就是函数之间的紧密耦合,如何进行解耦那,我们需要用到全局变量
进行函数之间值的传递, 可是这样真的解决问题了么?
这样的做法解决了一个问题,但是其他问题就会如雨后春笋一般冒出来,全局变量浪费内存,全局变量不安全,全局变量.... ,办法总比困难多,对于这样的数据共享我们其实可以使用订阅发布模式解决问题,但是订阅发布就万无一失了么?
2. 数据驱动模型的实现。
对于这样的需求我们往往需要使用订阅发布模式进行实现,但是订阅发布的封装又出现了n种方案,怎么办?
其实说到这里我们好像知道了一件事,问题的解决方案有了,但是我们实际上是欠缺一个统一的标准对我们的需求(函数与函数或者组件与组件之间的通讯)进行统一,这个标准不一定是最优的但是需要应用范围要广。
这个时候flux架构
出现了。
2. flux架构
flux架构就像是眼镜,你自然知道什么时候需要他
flux的横空出世好像是踏着五彩祥云的至尊宝,拯救万千前端于水火之中,但是这个至尊宝有点麻烦。
这是flux的架构图,你甚至不需要看懂,只需要知道整个架构的复杂程度还是非常高的,在flux之中想要更改数据仓库(store),我们需要调用dispather,在dispather之中手动调用 store之中的set方法,去更改状态,我们在开发企业级项目的时候,往往会维护一个非常庞大的store,这时候我们面对这样异常敏感而且不易排错的功能,可以说如履薄冰。
然后钢铁侠替代至尊宝来接你了。
3.redux
可以说redux就是flux架构的简化版,虽然还是很难,但是相对flux而言,redux把一些易错的操作进行了内部封装,这些封装使得报错风险急剧下降。
比较现成的东西就一定是量身订造的东西,虽然redux可以完全独立使用,可以配合任何框架实现基础功能但是我们最多的使用redux的地方还是在react之中,为啥那?
我们考虑的框架有两款,vue有比较现成的vuex, react那, fb的工程师更推荐使用redux,因为redux和react有更高的契合度有更丰富的合作生态如 react-redux
等。
4. redux使用详解
**part 1 : 看懂一张图 **
这张图基本说明了使用redux的全部内容,但是想要读懂这张图可能对于绝大多数小伙伴来说比较困难,怎么办呐? 不要放弃,我们先学一下基本操作,过一会再回头来看这张图。
-
redux 是干嘛的 ?
一句话解释 : 其实redux就是用来进行函数,组件之间数据共享的。
- redux怎样进行数据共享那 ?
一句话解释 : 就是把一个
对象
放进数据仓库
之中。- redux是否实现了发布订阅模式那 ?
一句话解释 : 实现了,这是后话。
所以我们大致了解了redux,那么我们用一个极简的翻译来看一下这个图。
这张图之中尤其要注意的是创建状态
到状态区分处理器
, 这两个部分之间的绿色箭头,在通过数据仓库时这个线是虚的,也就是说,本质上来讲是状态区分处理器最终将状态放入了数据仓库之中,可是为什么这条线要先经过数据仓库那 ?
敲黑板的重点来了 :
创状态这个功能其实会创建一个对象,这个对象会被
dispatch
方法直接派发到 数据仓库之中,但是数据仓库又需要去进行一个状态区分和处理,这个区分和处理是被直接集成在数据仓库之中的,相当于仓库分拣员 , 这个分拣员会自动将创建好的对象进行分类,分类后放入仓库。
数据仓库内部一旦发生改变了,那么视图就会发生相应的改变,这就是redux完整的流程。
但是这个完整的流程之中有非常之多的注意事项,如果你已经理解了以上的内容,那么就可以继续向下学习redux操作实例了。
为了方便操作,我们使用 create-react-app
脚手架工具搭建react环境,并使用 redux进行开发 , 编写redux实例,同时为了使用的安全和严谨,请务必参考 redux官方文档,其余野路子博客不建议查看。
redux官方文档
part 2 : 记得三个单词
1. action 这货就是我们刚才说的数据对象,这个对象就是JavaScript的单纯的对象。 就是下面这样的
var action = {
name : "一个对象"
}
2. action 还有一些小小的矫情,就是必须有某些东西,这个东西就叫做 `type`, 我们在创建action的时候通常会创建这样的一个对象
let action = {
type : MY_FIRST_ACTION,
payload : {
text : "随便写一些文本"
}
}
我们写的这个对象大概就是action了,但是我们想要创建茫茫多个action怎么办呐 ? 记得我们有一个创建器就好了,这个创建器叫做 actionCreater
3. actionCreate长成这样 , 我们通常定义 action之中type的时候会使用`常量`。
action.js
/*
* 常量定义,这个常量仅代表状态。
*/
export const INCREASE_NUMBER = "INCREASE_NUMBER";
export const REDUCE_NUMBER = "REDUCE_NUMBER";
export const INIT_NUMBER = "INIT_NUMBER";
export function increaseNumber(number){
return {
type : INCREASE_NUMBER,
payload : {
number
}
}
}
export function reduceNumber(number){
return {
type : REDUCE_NUMBER,
payload : {
number
}
}
}
4. 我们要设计一下我们当前state 的数据结构,这个数据结构方便我们后续的操作。
{
calculationNumber :{
type : INIT_NUMBER,
payload : {
number : 0
}
}
}
这大概就是一个及其简单的数据结构了。
5. 开发状态区分处理器 reducer
reducer接受两个参数 reducer( prevState, state) , 即为原始状态和更新状态 , 请务必记住reducer的特性,reducer是一个纯函数,这意味着 同样的输入,同样的输出,如果输入同样的内容那么输出一定是一致的,这就是纯函数。 在这里不能调用 Date.now() ,或者 Math.random() 等方法。
reducer.js
import {
INCREASE_NUMBER,
REDUCE_NUMBER,
INIT_NUMBER
} from "./action"
function calculation(state = initialState , action){
switch(action.type){
case INCREASE_NUMBER :
return Object.assign({},state,{
...action,
payload : {
number : state.payload.number ++
}
});
case REDUCE_NUMBER :
return Object.assign({},state,{
...action,
payload : {
number : state.payload.number --
}
});
default : return state;
}
}
export calculation;
6. 最后我们引入我们的主角
store.getState()
用来获取store完整数据。
store.subscribe()
用来监听完整数据。
import {createStore} from "redux";
import reducer from "./reducer";
let store = createStore(reducer);