React 项目中Redux 中间件的明白

媒介

React/Redux项目完毕后,当我在研讨react-router源码的时刻发明当中有一部份含中间件的头脑,所以才想把中间件从新梳理一遍;在之前看redux相识到中间件,redux层面中间件的明白对项目前期比较有协助,虽然项目中后期基础能够疏忽这层观点;现在对这部份的笔记从新梳理,这里只针对这个中间件做一个明白。

假如想进修项目标底层建立,提议先去进修官网redux案例,以后在进修react-router的运用

Redux 中间件引见

Redux 目标是供应第三方插件的形式,转变action -> reducer 的历程。变成 action -> middlewares -> reducer 。自身在项目中运用它转变数据流,完成异步 action ;下面会对日记输出做一个收场。

运用 Redux 中间件

Redux 中 applyMiddleware 的要领,能够运用多个中间件,这里先只写一个中间件,以日记输出中间件为例

//应用中间件做打印log
import {createStore,applyMiddleware} from 'redux';
import logger from '../api/logger';
import rootReducer from '../reducer/rootReducer';


let createStoreWithMiddleware = applyMiddleware(logger)(createStore);
let store = createStoreWithMiddleware(rootReducer);
// 也能够直接如许,能够参考createStore
// createStore(
//     rootReducer,
//     applyMiddleware(logger)
// )
export default store;

logger 中间件构造剖析

const logger = store => next => action => {
    let result = next(action); // 返回的也是一样的action值
    console.log('dispatch', action);
    console.log('nextState', store.getState());
    return result;
};

export default logger;

store => next => action =>{} 完成了三层函数嵌套,末了返回 next ,给下一个中间件运用,接下来把三层函数拆解;

从applyMiddleware源码最先剖析

///redux/src/applyMiddleware.js
export default function applyMiddleware(...middlewares) {
    return (createStore) => (reducer, initialState, enhancer) => {
        var store = createStore(reducer, initialState, enhancer)
        var dispatch = store.dispatch
        var chain = []
        var middlewareAPI = {
            getState: store.getState,
            dispatch: (action) => dispatch(action)
        }
        chain = middlewares.map(middleware => middleware(middlewareAPI))
        dispatch = compose(...chain)(store.dispatch)
        return {
            ...store,
            dispatch
        }
    }
}
最外层store
//源码剖析
chain = middlewares.map(middleware => middleware(middlewareAPI));

我们发明store是middlewareAPI,

//store
var middlewareAPI = {
    getState: store.getState,
    dispatch: (action) => dispatch(action)
}

然后就剩下

next => action => {
    let result = next(action); // 返回的也是一样的action值
    console.log('dispatch', action);
    console.log('nextState', store.getState());
    return result;
};
中间层next
//源码剖析
dispatch = compose(...chain)(store.dispatch)

先来剖析compose(…chain)

//compose源码
export default function compose(...funcs) {
    if (funcs.length === 0) {
        return arg => arg
    }

    if (funcs.length === 1) {
        return funcs[0]
    }

    const last = funcs[funcs.length - 1]
    const rest = funcs.slice(0, -1)
    return (...args) => rest.reduceRight((composed, f) => f(composed), last(...args))
}

compose应用Array.prototype.reduceRight的要领

//reduceRight遍历引见
[0, 1, 2, 3, 4].reduceRight(function(previousValue, currentValue, index, array) {
    return previousValue + currentValue;
}, 10);

//效果 10+4+3+2+1+0 = 20

由于我们这里的中间件就只要一个,所以没有运用到reduceRight直接返回,直接返回func[0](自身);再由compose(...chain)(store.dispatch),我们能够晓得next就是store.dispatch

(action) => {
    let result = store.dispatch(action); // 这里的next就是store.dispatch
    console.log('dispatch', action);
    console.log('nextState', store.getState());
    return result;
};

我们以后挪用的dispath就是触发的是上面这个函数(这里就单个中间件);

多个中间件

  • 经由过程上面的 applyMiddleware , compose 和中间件的构造,

  • 假定运用了以下的中间件: [A, B, C],这里我们运用es5的构造做剖析

  • 剖析action触发的完全流程

三个中间件

//A
function A(store) {
    return function A(next) {
        return function A(action) {
            /*...*/;
            next(action);
            /*...*/;
            return /*...*/;
        }
    }
}
//B
function B(store) {
    return function B(next) {
        return function B(action) {
            /*...*/;
            next(action);
            /*...*/;
            return /*...*/;
        }
    }
}
//C
function C(store) {
    return function C(next) {
        return function C(action) {
            /*...*/;
            next(action);
            /*...*/;
            return /*...*/;
        }
    }
}

经由过程chain = middlewares.map(middleware => middleware(middlewareAPI)),三个中间件的状况变化

//A
function A(next) {
    return function A(action) {
        /*...*/;
        next(action);
        /*...*/;
        return /*...*/;
    }
}
//B
function B(next) {
    return function B(action) {
        /*...*/;
        next(action);
        /*...*/;
        return /*...*/;
    }
}
//C
function C(next) {
    return function C(action) {
        /*...*/;
        next(action);
        /*...*/;
        return /*...*/;
    }
}

再由dispatch = compose(...chain)(store.dispatch),我们转化下

const last = C;
const rest = [A,B]
dispatch = rest.reduceRight(
    (composed, f) =>{
        return f(composed)
    }, 
    last(store.dispatch)
)

我们获得的效果

dispatch = A(B(C(store.dispatch)));

进一步剖析,我们获得的效果

dispatch = A(B(C(store.dispatch)));

//实行C(next),获得效果

A(B(function C(action) {/*...*/;next(action);/*...*/;return /*...*/;})); 
//此时的next = store.dispatch

//继承实行B(next)
A(function B(action) {/*...*/;next(action);/*...*/;return /*...*/;});    
//此时的next = function C(action) {/*...*/;next(action);/*...*/;return /*...*/;}

//继承实行A(next)
function A(action) {/*...*/;next(action);/*...*/;return /*...*/;};
//此时的next = function B(action) {/*...*/;next(action);/*...*/;return /*...*/;}

一个action触发实行递次,A(action) -> B(action) -> C(action) -> store.dispatch(action)(临盆最新的 store 数据);

假如next(action)下面另有须要实行的代码,继承实行 C(next 后的代码)->B(next 后的代码)->A(next 后的代码)

总结:先从内到外天生新的func,然后由外向内实行。原本我们能够直接运用store.dispatch(action),然则我们能够经由过程中间件对action做一些处置惩罚或转换,比方异步操纵,异步回调后再实行next;如许的设想很奇妙,只要守候next,才能够继承做操纵,和日常平凡直接异步回调又有些不一样

项目实践 ->异步

我们晓得redux中actions分为actionType,actionCreator,然后在由reducer举行修正数据;

官方例子中async直接在actionCreator做了ajax要求;

我们把ajax放入中间件触发下面要讲的与官方real-world相似

我这边运用redux-thunk

applyMiddleware(reduxThunk, api)

先来看看redux-thunk的源码

function createThunkMiddleware(extraArgument) {
    return ({ dispatch, getState }) => next => action => {
        if (typeof action === 'function') {//从新分发
            return action(dispatch, getState, extraArgument);
        }
        return next(action);//通报给下一个中间件
    };
}

const thunk = createThunkMiddleware();
thunk.withExtraArgument = createThunkMiddleware;

export default thunk;

如许一来我们能够把异步写成一个复用的actionCreator;

import * as types from '../../constants/actions/common';

export function request(apiName, params, opts = {}) {
    return (dispatch, getState) => {
        let action = {
            'API': {
                apiName: apiName,
                params: params,
                opts: opts
            },
            type: types.API_REQUEST
        };
        return dispatch(action);
    };
}


//其他地方挪用复用的要领以下:
export { request } from './request';

一般的写法,不是异步的,就是之前的写法

export function cartSelect(id) {
    return { 
        type: types.CART_MAIN_SELECT, 
        id
    };
}

然后就是下一个中间件的处置惩罚 api.js

//自身封装的ajax,能够运用别的,比方isomorphic-fetch
import net from 'net';
//项目中悉数的接口,相当于一个关于异步的actionType有一个对应的后端接口
import API_ROOT from 'apiRoot';

export default store => next => action => {
    let API_OPT = action['API'];

    if (!API_OPT) {
        //我们商定这个没声明,就不是我们设想的异步action,实行下一个中间件
        return next(action);
    }

    let ACTION_TYPE = action['type'];
    let { apiName, params = {} , opts = {} } = API_OPT;
    /**
     * 假如有通报localData,就不会触发ajax了,直接触发_success
     * 当前也能够传其他参数
     */
    let { localData } = opts;
    let {
        onSuccess,
        onError,
        onProgress,
        ajaxType = 'GET',
        param
    } = params;
    // 触发下一个action
    let nextAction = function(type, param, opts) {
        action['type'] = type;
        action['opts'] = opts;
        delete param['onSuccess'];
        delete param['onError'];
        const nextRequestAction = {...action,...param}
        return nextRequestAction;
    };

    params={
        ...params,
        data: null
    };
    // 触发正在要求的action
    let result = next(nextAction(apiName + '_ON', params, opts));
    net.ajax({
        url: API_ROOT[apiName],
        type: ajaxType,
        param,
        localData,
        success: data => {
            onSuccess && onSuccess(data);
            params={
                ...params,
                data
            };
            //触发要求胜利的action
            return next(nextAction(apiName + '_SUCCESS', params, opts));
        },
        error: data => {
            onError && onError(data);
            //触发要求失利的action
            return next(nextAction(apiName + '_ERROR', params, opts));
        }
    });

    return result;
};

强调一点:项目中悉数的接口,相当于一个关于异步的actionType有一个对应的后端接口,所以我们才能够经由过程API_ROOT[apiName]找到这个接口

以cart为列子(下面是对应的每一个文件):

actionType:

//异步
export const CART_MAIN_GET = 'CART_MAIN_GET';
//非异步
export const CART_MAIN_SELECT = 'CART_MAIN_SELECT';

api:

const api = {
    'CART_MAIN_GET':'/shopping-cart/show-shopping-cart'
};
export default api;

APIROOT修正:

import cart from './api/cart';
const APIROOT = {
    ...cart
};
export default API;

actionCreator:

//项目中运用redux的bindActionCreators做一个一致的绑定,所以在这里零丁引入
export { request } from './request';
//下面黑白异步的要领
export function cartSelect(id) {
    return { 
        type: types.CART_MAIN_SELECT, 
        id
    };
}

项目中提议构造是如许的:

let url = types.CART_MAIN_GET;
let param = {};
let params = {
    param: param,
    ajaxType: 'GET',
    onSuccess: (res) => {
        /*...*/
    },
    onError: (res) => {
        /*...*/
    }
};
request(url, params, {});

其对应的reducers就是下面

import * as types from '../constants/actions/cart';
const initialState = {
    main:{
        isFetching: 0,//是不是已猎取 
        didInvalidate:1,//是不是失效
        itemArr:[],//自定义模版
        itemObj:{},//自定义模版数据
        header:{}//头部导航
    }
};
export default function(state = initialState, action) {
    let newState;
    switch (action.type) {
        case types.HOME_MAIN_GET + '_ON'://能够不写
            /*...*/
            return newState;
        case types.HOME_MAIN_GET + '_SUCCESS':
            /*...*/
            return newState;
        case types.HOME_MAIN_GET + '_ERROR'://能够不写
            /*...*/
            return newState;
        default:
            return state;
    }
};

异步,数据考证都能够经由过程中间件做处置惩罚;援用Generator,Async/Await,Promise处置惩罚,能够参考社区中的一些其他体式格局,比方:

    原文作者:Deot
    原文地址: https://segmentfault.com/a/1190000007843340
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞