美文网首页
redux源码解析之applyMiddleware

redux源码解析之applyMiddleware

作者: summer_味道制造 | 来源:发表于2018-03-04 15:14 被阅读0次

    前言

    项目中一直使用redux来进行状态管理,对于redux中间件传递这一块一直不是很理解,只懂得使用不懂得原理,今天来分析一下applyMiddleware的源码。

    加载中间件的加载

    首先我们来看一下在项目中是如何加载中间件的

    import { createStore, applyMiddleware } from 'redux';
    imoprt thunk from 'redunx-thunk';
    import todos from './reducers';
    
    const createStoreWithMiddleware = applyMiddleware(thunk)(createStore);
    const store = createStoreWithMiddleware(todos,[ 'Use Redux' ]);
    

    如果你用过redux就会知道,加载中间件会使用到redux提供的工具方法:applyMiddleware,将你需要使用的中间件作为参数传递给applyMiddleware,它会返回一个函数,然后再将createStore传递进,最后得到一个新的createStore;

    下面我们来看一下applyMiddleware的源码(可通过gitHub获取)

    export default function applyMiddleware(...middlewares) {
      return createStore => (...args) => {
        const store = createStore(...args)
        let dispatch = () => {
          throw new Error(
            `Dispatching while constructing your middleware is not allowed. ` +
              `Other middleware would not be applied to this dispatch.`
          )
        }
        let chain = [] //用于存放中间件
    
        const middlewareAPI = {
          getState: store.getState,
          dispatch: (...args) => dispatch(...args)
        }
        chain = middlewares.map(middleware => middleware(middlewareAPI))
        dispatch = compose(...chain)(store.dispatch)
    
        return {
          ...store,
          dispatch
        }
      }
    }
    

    代码非常少,20行左右,下面我们就开始分析代码吧

    createStore => (...args) =>{....}
    

    我们知道applyMiddleware返回的是一个函数,从使用的方法可以看到这里createStore,就是我们一开始传递进去的那个createStore,它又返回一个函数,这个就是上面说的createStoreWithMiddleware,简单来说,最后调用的是

    (reducer,initState)=>{
      const store = createStore(reducer,initState);
    }
    
    let dispatch = () => {
          throw new Error(
            `Dispatching while constructing your middleware is not allowed. ` +
              `Other middleware would not be applied to this dispatch.`
          )
        }
    let chain = []
    

    这几行注释已经说得很清楚了,就不细说了

     const middlewareAPI = {
          getState: store.getState,
          dispatch: (...args) => dispatch(...args)
        }
        chain = middlewares.map(middleware => middleware(middlewareAPI))
        dispatch = compose(...chain)(store.dispatch)
    

    这里声明了一个变量middlewareAPI,它是一个对象,有两个方法(从这里开始非常的绕,我已经有点晕了。使用了大量的高阶函数)
    1.getState 这个方法是对store中的getStat方法的引用
    2.dispatch 从上面可以看到已经定义了一个dispatch,规定不允许在加载中间时调用,在下面又重新修改了指向。(表示对store.dispatch的封装)

    chain = middlewares.map(middleware => middleware(middlewareAPI));
    

    这行代码对传递进行的中间件数组进行一次map,map中调用中间件,并将middlewareAPI作为参数传递给中间件,这里我们就拿redux-thunk来看看中间件是长什么样子的

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

    我们通常使用redux-thunk来进行ajax操作,在上面看到进行map操作时将middlewareAPI传递到了redux-thunk,然后它返回了一个函数,用es5表示就是下面这样子的

    funciton thunkMiddleware(middlewareAPI){
      return function(next){ 
          return function(action){
             typeof action === 'function' ?
             action(dispatch, getState) :
             next(action);     
         }
      }
    }
    我们在回到上面
    
    chain = middlewares.map(middleware => middleware(middlewareAPI))
    

    那么现在我们可以知道,这个东西返回了一个像这样的函数

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

    在接着往下看之前

    dispatch = compose(...chain)(store.dispatch);
    

    在说这里之前,我们先看一下compose的源码

    export default function compose(...funcs) {
      if (funcs.length === 0) {
        return arg => arg
      }
      if (funcs.length === 1) {
        return funcs[0]
      }
      return funcs.reduce((a, b) => (...args) => a(b(...args)))
    }
    

    之前说到chain是一个组数,那么chain === funcs,这个方法首先判断chain数组的长度,如果为0就就返回一个什么都不做的函数(为0就意味着没有中间件),如果直接一个中间件就返回这个中间件并调用它,并且将store.dispatch传递进去,并赋值给dispatch

    return funcs.reduce((a, b) => (...args) => a(b(...args))
    

    我们重点来看这行代码,reduce是数组的的一个方法,它的第一个参数是一个函数,这个函数接受4个参数,分别是previousValu(上一次的值),currentValue(当前值),currentIndex(当前值的索引),array;reduce 为数组中的每一个元素依次执行回调函数(不包括数组中被删除或从未被赋值的元素)。

    这里的args就是外面传递的store.dispatch;这段代码写成es5的形式大概是下面这样(...agrs是es6语法,如果有不清楚的请参阅# ECMAScript 6 入门)

    funcs.reduce(function(a,b){
      retrurn function(... args]){
        return a(b(... args))
      }
    })
    

    从代码中可以看出(注意两个...ags,代表的含义不同,第一个代表的是剩余参数,第二个agrs代表的是展开数组),对每个元素执行了回调函数,它又再次返回了一个新的函数(真的无力,嵌套太深了)

    return a(b(... args))
    

    继续来看这里,这里是依次执行了中间件,并将返回的函数传递给下一个中间件
    如果有数组[fn1,fn2,fn3,fn4],那么返回 fn1(fn2(fn3(fn4(..args))))
    还记得thunk的代码吗,这里我们还是拿thunk来举例,看下面代码

     return ({ dispatch, getState }) => next => action => { 
        if (typeof action === 'function') {
          return action(dispatch, getState, extraArgument);
        }
        return next(action);
      };
    

    如果只有一个中间件,那么这里的next表示的是store.dispatch,否则表示其它的中间件。
    总的来说,如果我们只使用一个中间,那么最后得到的dispatch方法就是

    action => { 
        if (typeof action === 'function') {
          return action(dispatch, getState, extraArgument);
        }
        return next(action);
      };
    

    相关文章

      网友评论

          本文标题:redux源码解析之applyMiddleware

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