Redux:全揭秘与入坑指北(中)

  • 什么是Redux中间件
  • 存在中间件时仓库的初始化
  • 创建一个中间件
  • applyMiddleware.js
  • compose.js
  • thunk
  • promise

pre-notify

previously:Redux:全揭秘与入坑指北(上)

上一篇说了下creatStore和react-redux,这一篇主要说一下中间件

本来想画全流程解析图的,emmm...没找到好的工具,可以参考下网上流传的几张

点我

什么是Redux中间件

中间件是插入在用户发射 action动作之后到 reducer接收到这个动作之前这个时机的处理器,它能完成一些额外的逻辑。

存在中间件时仓库的初始化

平时我们是这么初始化一个有中间件的仓库的

import {createStore,applyMiddleware} from 'redux';

let store = createStore(reducer,{astate:..,bstate:...,...},applyMiddleware(xx,yy,zz));
复制代码

其实内部是这样调用的

let store = applyMiddleware(xxx,yyy,...)(createStore)(reducer,{astate:..,bstate:...,...})
复制代码

emmm,原理是这样的

export default function createStore(reducer, preloadedState, enhancer){ // 增强器
  if(enhancer && typeof enhancer === 'function'){
    return enhancer(createStore)(reducer,preloadedState)
  }
  ...
复制代码

So,我们现在知道了,如果有中间件,会先执行applyMiddleware应用中间件这个方法,并且将createStorereducerpreloadedState传到applyMiddleware这个方法里面,在里面初始化仓库。

创建一个中间件

我们再来看看平时我们是怎么定义一个中间件的

let logger = ({dispatch,getState})=>next=>action=>{
    //do shome thing else
    console.log(1);
    next(action);
    console.log(11);
    //do some thing else agin
}
复制代码

它等价于

let logger = function({dispatch,getState}){
    return function(next){
    	return function(action){
    	    //do shome thing else
            console.log(1);
            next(action);
            console.log(11);
            //do some thing else agin
        }
    }
}
复制代码

其中,getState 就是createStore初始化一个仓库时原本导出的getState,action也还是用户所发射的那个reducer所接受的那个action,

dispatch不再是createStore所导出的那个了,而是经过中间件包装后的dispatch。

另外next其实是另外一个中间件,和Koa中的中间件一样我们需要手动调用next来让队列中的中间件一次执行。并且中间件的运行及结果依然遵循洋葱模型

我们来看这样一个栗子,假若我们有两个中间件

//中间件1
let middle1 = function({dispatch,getState}){
    return function(next){
    	return function(action){
    	    //do shome thing else
            console.log(1);
            next(action);
            console.log(11);
            //do some thing else agin
        }
    }
}

//中间件2
let middle2 = function({dispatch,getState}){
    return function(next){
    	return function(action){
    	    //do shome thing else
            console.log(2);
            next(action);
            console.log(22);
            //do some thing else agin
        }
    }
}
复制代码

然后我们这样初始化仓库时这样注册他们

let store = applyMiddleware(middle2,middle1)(createStore)(reducer);
复制代码

那么当我们发射一个动作时,比如说

<button onClick={this.props.increment}>+</button>
复制代码

那么此时就会调用派发,只不过此时的派发方法是经过先经过middle1包装后又经过middle2包装的方法。它会先执行middler2额外添加的一部分代码,然后再执行middle1额外添加的一部分代码,然后才会真正的派发,派发完以后又会将middle1剩下的代码执行完,最后是middle2剩下的代码。

执行结果会是:先输出2,再输出1,再执行store.dispatch,再输出11,最后输出22

接下来,我们来看这样的模型具体是怎样实现的。

applyMiddleware.js

这个方法最后会将原本的store.dispatch给替换成新的dispatch,这个新的dispatch是则是我们上面所说的经过中间件层层包装后的新的dispatch。

//applyMiddleware.js

export default function(...middlewares){
    return function(createStore){
    	return function(reducer,preloadedState){
    	    let store = createStore(reducer,preloadedState);
            let dispatch;
            let middlewareAPI = {
            	getState:store.getState
                ,dispatch:action=>dispatch(action)
            }
            middlewares = middlewares.map(middleware(middlewareAPI));
            dispatch = compose(...middlewares)(store.dispatch);
            return {...store,dispatch};
        }
    }
}
复制代码

compose.js

applyMiddleware.js中我们用到了一个compose方法,

这个方法会把中间件串行起来,并且一层包一层,按照applyMiddleware()调用时中间件注册的顺序,先注册的会成为洋葱模型最外面的一层,后注册的则往里一层,最里面是原本的store.dispatch。

//compose.js

export default function(...fns){
    return function(...args){
    	let last = fns.pop();
        return fns.reduceRight((val,fn)=>{
    	    return fn(val);
        },last(...args));
    }
}

//--- --- ---

//高逼格版
export default function(...fns){
    if(fns.length === 1)return fns[0];
    return fns.reduce((a,b)=>(...args)=>a(b(...args)));
}
复制代码

为了便于理解compose函数的作用,请看一下以下的例子

function add1(str){
    return str+'com';
}
function add2(str){
    return str+'po';
}
function add3(str){
    return str+'se!';
}

add3(add2(add1('hello,'))); //输出:hello,compose!

//以上等价于
let add = compose(add3,add2,add1);
add('hello,');
复制代码

So,上面的add其实就是我们的中间件

thunk

...
,thunkIncrement(){ 
    return function(dispatch,getState){
      setTimeout(function(){
        dispatch({type:types.INCREMENT,payload:1})
      },1000);
    }
}
...
复制代码
//处理自定义异步操作的中间件

let thunk = ({dispatch,getState})=>next=>action=>{ //处理函数的
  if(typeof action == 'function'){
    action(dispatch,getState);  //说明是一个异步动作,将dispatch等传给这个异步动作的函数,在异步有结果后再执行派发改变原本状态。
  }else{
    next(action); //匹配下一个中间件
  }
};
复制代码

promise

...
//1)
,promiseIncrement(){
    return new Promise(function(resolve,reject){
     setTimeout(function(){
       resolve({type:types.INCREMENT,payload:1}); //reject无法处理
     },1000)
    });
}
//2)
,payloadIncrement(){
    return {
      type:types.INCREMENT
      ,payload:new Promise(function(resolve,reject){
        setTimeout(function(){
          if(Math.random()>.5){
            resolve(100);
          }else{
            reject(-100);
          }
        },1000)
      })
    }
}
...
复制代码
 //处理promise的中间件
 
let promise = ({dispatch,getState})=>next=>action=>{
  if(action.then&&typeof action.then === 'function'){ //是一个promise
    action.then(dispatch);
  }else if(action.payload&&action.payload.then){
    action.payload.then(payload=>dispatch({...action,payload}),payload=>dispatch({...action,payload}));
  } else{
    next(action);
  }
};
复制代码
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值