美文网首页
redux中间件

redux中间件

作者: java高并发 | 来源:发表于2019-01-27 20:51 被阅读6次

    一、中间件的概念

    redux是有流程的,那么,我们该把这个异步操作放在哪个环节比较合适呢?

    • Reducer?纯函数只承担计算State功能,不适合其它功能。
    • View?与State一一对应,可以看做是State的视觉层,也不适合承担其它功能。
    • Action?它是一个对象,即存储动作的载体,只能被操作。

    其实,也只有dispatch能胜任此重任了。那么怎么在dispatch中添加其它操作呢?

    let next = store.dispatch;
    store.dispatch = function(action){
       console.log('老状态 ',store.getState());
       next(action);
       console.log('新状态 ',store.getState());
    }
    复制代码
    

    示例中可以看出,我们对store.dispatch重新进行了定义,在发送action的前后,做了打印。

    这是中间件的大致雏形,真实的中间件要比这么复杂多了

    二、中间件的用法

    我们在这里先看看中间件是怎么使用,下面我们一步步剖析每个细节。

    import {applyMiddleware,createStore} from 'redux';
    import reduxLogger form 'redux-logger';
    
    const store = createStore(reducer,inital_state,applyMiddleware(thunk, promise,reduxLogger));
    
    复制代码
    

    代码中有两点需要注意:

    • 1、createStore方法可以整个应用的初始状态作为参数 内部是这么处理的
    let state = inital_state;
    复制代码
    
    • 2、中间件的参数次序有讲究。下面我会把这个问题讲明白。

    三、applyMiddleware

    Middleware可以让你包装storedispatch方法来达到你想要的目的。同时,middleWare还拥有“可组合”这一关键特性。多个middleWare可以被组合到一起使用,形成middleWare链,依次执行。其中每个middleware不需要关心链前后的的middleWare的任何信息。

    function applyMiddleware(...middlewares){
        return function(createStore){
            return function(reducer){
                //引入store
                let store = createStore(reducer);
                let dispatch = store.dispatch;
                let middlewareAPI = {
                    getState:store.getState,
                    // 对dispatch进行包装
                    dispatch:action=>dispatch(action)
                }
                //每个中间件都是这种模型  ({ getState, dispatch }) => next => action
                chain = middlewares.map(middleware=>middleware(middleAPI));
                dispatch = compose(...chain)(store.dispatch);
                // dispatch被改装后,返回store
                return{...store,dispatch};
            }
        }
    }
    复制代码
    

    上面代码中,所有中间件都被放进了一个数组chain,然后嵌套执行,最后执行store.dispatch。中间件内部middlewaAPI可以拿到getStatedispatch这两个方法。

    ...middleware:遵循Redux middleware API的函数。每个middleware接受StoredispatchgetState函数作为命名参数,并返回一个函数。该函数会被传入成为next的下一个middleWare 的dispatch方法,并返回一个接收action的新函数,这个函数可以直接调用next(action),或者在其他需要的时刻调用,甚至根本不去调用它。

    所以,接下来,我们就能看到middleware的函数签名是({ getState, dispatch }) => next => action

    其实,它的本质就是包装sotre中的dispatch

    上面代码中,还用到了compose方法,我们来看看compose是怎么是实现的?

    compose

    先看下面一个栗子:

    function add1(str){
       return str+1;
    }
    function add2(str){
        return str+2;
     }
     function add3(str){
        return str+3;
     }
     let result = add3(add2(add1('好吃')));// 好吃123;
    
    复制代码
    

    这中写法调用起来,一层套一层,是不是看着很不爽,我们简化一下:

    function compose(...fns){
        if(fns.length==1)
         return fns[0];
       return function(...args){
        let last = fns.pop();
        return fns.reduceRight((prev,next)=>{
             return  next(prev);  
        },last(...args));
       }
     }
     let add = compose(add3,add2,add1);//
     let result = add('好吃');// 好吃123
     // 上面的代码其实就是redux3.6.0版本中compose的实现方式
    复制代码
    

    看看这个代码是不是用起来,很干练一些。其实还可以简化

     function compose(...fns){
      if(fns.length==1)
         return fns[0];
       return fns.reduce((a,b)=>(...args)=>a(b(...args)));//add3(add2(add1('好吃')))
     }
     let add = compose(add3,add2,add1);//
     let result = add('好吃');// 好吃123
     // 这是redux3.6.0版本之后的compose实现方式,一直沿用至今。
    复制代码
    

    至于为什么applyMiddleWare的参数有顺序,这里给出了答案。

    四、Applymiddleware的三个常用参数

    4.1、日志记录

    使用 Redux 的一个益处就是它让 state 的变化过程变的可预知和透明。每当一个 action 发起完成后,新的 state 就会被计算并保存下来。State 不能被自身修改,只能由特定的 action 引起变化。

    试想一下,当我们的应用中每一个 action 被发起以及每次新的 state 被计算完成时都将它们记录下来,岂不是很好?当程序出现问题时,我们可以通过查阅日志找出是哪个 action 导致了 state 不正确。

    图片的效果是不是很期待啊!!!

    我们先来手动实现一版。

    // 记录所有被发起的action和新的state
    let next = store.dispatch;
    store.dispatch = function(action){
       console.log('老状态 ',store.getState());
       next(action);
       console.log('新状态 ',store.getState());
    }
    复制代码
    

    还是上面的示例,我们来做个修改

    let logger = function({ getState, dispatch }){
       return function(next){// 这里的next可以理解为store.dispath,本质上就是调用 middleware 链中下一个 middleware 的 dispatch。
          return function(action){
            console.log('老状态1 ',getState());
            next(action);//派发动作
            console.log('新状态1 ',getState());
        }
        }
    }
    // 高逼格写法
    let logger = ({ getState, dispatch }) => next => action => {
      console.log('老状态1 ',getState());
      next(action)
      console.log('新状态1 ',getState());
    }
    复制代码
    

    4.2、redux-thunk 中间件

    redux-thunkredux官方文档中用到的异步组件,实质就是一个redux中间件,一个封装表达式的函数,封装的目的就是延迟执行表达式。

    redux-thunk是一个通用的解决方案,其核心思想是让action可以变成一个thunk,这样的话,同步情况:dispatch(action),异步情况:dispatch(thunk)

    下面是redux-thunk的实现:

    let thunk = ({dispatch,getState})=>next=>action=>{
        if(typeof action == 'function'){
            action(dispatch,getState);
        }else{
            next(action);//这里可以理解为dispatch(action),本质上就是调用 middleware 链中下一个 middleware 的 dispatch。
        }
    }
    复制代码
    

    使用redux-thunk

    const store = createStore(  
      reducer,
      applyMiddleware(thunk)
    );
    复制代码
    

    然后我们实现一个thunkActionCreator

        //过一秒加1
        export function thunkActionCreator(payload){
            return function(dispatch,getState){
                setTimeout(function(){
                    dispatch({type:types.INCREMENT,payload:payload});
                },1000);
            }
        },
    复制代码
    

    最后,在组件中dispatch thunk

    this.dispatch(thunkActionCreator(payload));
    复制代码
    

    4.3、redux-promise

    redux-promise也是延迟执行的表达式,它是解决异步的另外一种方案。

    redux-thunk和核心思想是把action变成thunk,而redux-promise的核心思想是让action返回一个promise对象。

    这个中间件使得store.dispatch方法可以接收Promise对象作为参数。这时 ,action 有两种写法:

    写法一、返回值是一个Promise对象。

    function promiseIncrement(payload){
     //  return {type:types.INCREMENT,payload:payload}  以前是这种写法
        return new Promise(function(resolve,reject){
          setTimeout(function(){
            resolve({type:types.INCREMENT,payload:payload});
          },1000);
        });
     },
    复制代码
    

    写法二,action 对象的payload属性是一个Promise对象,这需要从

    function payloadIncrement(){
        return {
            type:types.INCREMENT,
            payload: new Promise(function(resolve,reject){
                setTimeout(function(){
                    if(Math.random()>.5){
                        resolve(100);
                    }else{
                        reject(-100);
                    }
                },1000)
            })
        }
    }
    复制代码
    

    下面我们来看看 redux-promise是怎么实现的,就会明白它内部是怎么操作的.

    let promise = ({dispatch,getState})=>next=>action=>{
        if(action.then && typeof action.then == 'function'){
            action.then(dispatch);
            // 这里的dispatch就是一个函数,dispatch(action){state:reducer(state,action)};
        }else if(action.payload&& action.payload.then&& typeof action.payload.then == 'function'){
            action.payload.then(payload=>dispatch({...action,payload}),payload=>dispatch({...action,payload}));
        }else{
            next(action);
        }
    }
    复制代码
    

    上面的代码可以看出,如果Action本身就是一个Promise,它resolve以后的值应该是一个Action对象,会被dispatch方法送出action.then(dispatch);如果Action对象的 payload属性是一个Promise对象,那么无论resolvereject,dispatch 方法都会发出Action

    需要dubbo视频教程的朋友。欢迎私信我领取资料。

    相关文章

      网友评论

          本文标题:redux中间件

          本文链接:https://www.haomeiwen.com/subject/ywpnjqtx.html