Redux学习之一:何为middleware?

接触到redux有半个月之久了,最初是被各类概念绕的云里雾里,可是最近本身实现了一个demo以后,才发现redux是如此简洁,简洁到源代码就五部分组成,最大的createStore.js也就151行代码而已。node

createStore.js
applyMiddleware.js
bindActionCreators.js
combineReducers.js
compose.js

redux的魅力在于其简洁和FP编程思想。像reducers的设计、curry编程风格等都散发出淡淡的FP的味道。
下面言归正传,进入middleware部分。下面由几个问题领着你们看看middleware的真面目。git

1.什么是middleware?

学过nodejs的人对middleware必定不会陌生。在nodejs里面,middleware是req和res之间的中间层,能够作不少事情。但在redux里面,middleware又是什么呢。
https://github.com/rackt/redux/blob/master/docs/advanced/Middleware.md
middleware文档里面有这样一句话:github

It provides a third-party extension point between dispatching an
action, and the moment it reaches the reducer.编程

不难理解,在redux里,middleware是发送action和action到达reducer之间的第三方扩展,也就是中间层。也能够这样说,middleware是架在action和store之间的一座桥梁。redux

2.为何要引入middleware?

也许有人会问,到底middleware有什么用?
这就要从action提及。在redux里,action仅仅是携带了数据的普通js对象( plain JavaScript objects)。action creator返回的值是这个action类型的对象。而后经过store.dispatch()进行分发……数组

action ---> dispatcher ---> reducers浏览器

同步的状况下一切都很完美……
若是遇到异步状况,好比点击一个按钮,但愿2秒以后更新视图,显示消息“Hi”。咱们可能这么写ActionCreator:app

var asyncSayActionCreator = function (message) {
    setTimeout(function () {
        return {
            type: 'SAY',
            message
        }
    }, 2000)
}

这会报错,由于这个asyncSayActionCreator返回的不是一个action,而是一个function。这个返回值没法被reducer识别。
你们可能会想到,这时候须要在action和reducer之间架起一座桥梁……异步

3.middleware如何工做?

咱们看看redux-thunk的代码:async

export default function thunkMiddleware({ dispatch, getState }) {
  return next => action =>
    typeof action === 'function' ?
      action(dispatch, getState) :
      next(action);
}

仅仅是区区的6行代码,以致于我第一次看代码的时候怀疑是否是看错了,但其实它就这么简单。一个三目符,若是action是一个函数,执行这个action函数,若是不是函数,执行next函数。
可能你们换是不懂,结合middleware的应用便会一目了然:

const finalCreateStore=applyMiddleware(thunkMiddleware)(createStore)
const store = finalCreateStore(reducer)

这就是咱们最常使用middleware的代码。把源码中的next换成createStore,若是action是一个函数(这里的action是改造后的ActionCreator),便会执行这个action(dispatch, getState)函数。

var asyncSayActionCreator = function (message) {
    return function (dispatch) {
        setTimeout(function () {
            dispatch({
                type: 'SAY',
                message
            })
        }, 2000)
    }
}

这里的action是return的函数:

function (dispatch) {
        setTimeout(function () {
            dispatch({
                type: 'SAY',
                message
            })
        }, 2000)
    }

若是action返回的不是函数,即返回的是action对象的话,执行createStore函数的dispatch方法。
有了middleware以后,数据流动的方向变为:

action ---> dispatcher ---> middleware 1 ---> middleware 2 ---> reducers

4.applyMiddleware都作了什么?

redux2.0对applyMiddleware作了柯里化处理。

import compose from './compose';
export default function applyMiddleware(...middlewares) {
  return (next) => (reducer, initialState) => {
    var store = next(reducer, initialState);
    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
    };
  };
}

其实最核心的代码也就是下面两句:

chain = middlewares.map(middleware => middleware(middlewareAPI));
dispatch = compose(...chain)(store.dispatch);

前一句是执行middlewares数组里的每个中间件,下一句是将chain柯里化,合成一个函数。

5.本身的middleware怎么写?

学了这些,咱们能够本身写一个middleware练练手。
首先在项目下建个middlewares的文件夹,新建一个callTraceMiddleware.js来追踪函数的调用过程。
在funCallTrace.js添加以下代码:

export default function callTraceMiddleware ({dispatch,getState}){
    return next=> action =>{
        console.trace();
        return next(action);
    }
}

而后在调用中间件部分添加中间件:

const createStoreWithMiddleware = applyMiddleware(
  thunkMiddleware,
  loggerMiddleware,
  callTraceMiddleware
)(createStore);

这样咱们运行在浏览器窗口就能够看到打印的函数调用轨迹。是否是很简单……

总结

redux的middleware是对action进行扩展处理,这样丰富了应用需求。以上是我对redux的我的理解,如发现错误恳请批评指正。

相关文章
相关标签/搜索