React 项目中Redux 中间件的理解
前言React/Redux项目结束后,当我在研究react-router源码的时候发现当中有一部分含中间件的思想,所以才想把中间件重新梳理一遍;在之前看redux了解到中间件,redux层面中间件的理解对项目前期比较有帮助,虽然项目中后期基本可以忽略这层概念;现在对这部分的笔记重新梳理,这里只针对这个中间件做一个理解。
Redux 中间件介绍
使用 Redux 中间件Redux 中 //利用中间件做打印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;
从applyMiddleware源码开始分析///redux/src/applyMiddleware.js export default function applyMiddleware(...middlewares) { return (createStore) => (reducer,initialState,enhancer) => { var store = createStore(reducer,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',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直接返回,直接返回 (action) => { let result = store.dispatch(action); // 这里的next就是store.dispatch console.log('dispatch',store.getState()); return result; }; 我们之后调用的 多个中间件
三个中间件 //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 /*...*/; } } } 通过 //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 /*...*/; } } 再由 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触发执行顺序, 如果 总结:先从内到外生成新的func,然后由外向内执行。本来我们可以直接使用 项目实践 ->异步我们知道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',opts)); net.ajax({ url: API_ROOT[apiName],type: ajaxType,localData,success: data => { onSuccess && onSuccess(data); params={ ...params,data }; //触发请求成功的action return next(nextAction(apiName + '_SUCCESS',opts)); },error: data => { onError && onError(data); //触发请求失败的action return next(nextAction(apiName + '_ERROR',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,{}); 其对应的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处理,可以参考社区中的一些其他方式,比如:
(编辑:李大同) 【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容! |