资讯专栏INFORMATION COLUMN

Redux中间件原理详解

Shihira / 3445人阅读

摘要:当你应用了中间件,在触发一个操作的时候,操作就会经过先经过中间件,最终再形成。以其中两个中间件为例,说明下,一个触发一个动作的时候,代码的执行逻辑。

为了解析中间件,先看一下几个中间件是什么样子,怎么用,运行起来的原理是什么?

1、中间件是什么样子的 1.2 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;
1.2promise中间件
import isPromise from "is-promise";
import { isFSA } from "flux-standard-action";

export default function promiseMiddleware({ dispatch }) {
  return next => action => {
    if (!isFSA(action)) {
      return isPromise(action) ? action.then(dispatch) : next(action);
    }

    return isPromise(action.payload)
      ? action.payload
          .then(result => dispatch({ ...action, payload: result }))
          .catch(error => {
            dispatch({ ...action, payload: error, error: true });
            return Promise.reject(error);
          })
      : next(action);
  };
}
1.3logger中间件
const defaultLogger = ({ dispatch, getState } = {}) => {
  if (typeof dispatch === "function" || typeof getState === "function") {
    return createLogger()({ dispatch, getState });
  }
};
function createLogger(options = {}) {
  const loggerOptions = Object.assign({}, defaults, options);

  const {
    logger,
    stateTransformer,
    errorTransformer,
    predicate,
    logErrors,
    diffPredicate,
  } = loggerOptions;

  if (typeof logger === "undefined") {
    return () => next => action => next(action);
  }

  import { logger } from "redux-logger"
  const store = createStore(
      reducer,
      applyMiddleware(logger))
    
  import { createLogger } from "redux-logger"
  const logger = createLogger({
      // ...options
    });
  const store = createStore(
      reducer,
      applyMiddleware(logger));
      
  return () => next => action => next(action);
  }

  const logBuffer = [];

  return ({ getState }) => next => (action) => {
    if (typeof predicate === "function" && !predicate(getState, action)) {
      return next(action);
    }
    const logEntry = {};

    logBuffer.push(logEntry);

    logEntry.started = timer.now();
    logEntry.startedTime = new Date();
    logEntry.prevState = stateTransformer(getState());
    logEntry.action = action;

    let returnedValue;
    if (logErrors) {
      try {
        returnedValue = next(action);
      } catch (e) {
        logEntry.error = errorTransformer(e);
      }
    } else {
      returnedValue = next(action);
    }

    logEntry.took = timer.now() - logEntry.started;
    logEntry.nextState = stateTransformer(getState());

    const diff = loggerOptions.diff && typeof diffPredicate === "function"
      ? diffPredicate(getState, action)
      : loggerOptions.diff;

    printBuffer(logBuffer, Object.assign({}, loggerOptions, { diff }));
    logBuffer.length = 0;

    if (logEntry.error) throw logEntry.error;
    return returnedValue;
  };
}
export { defaults, createLogger, defaultLogger as logger };

export default defaultLogger;
2、怎么使用中间件
const store = createStore(rootReducer, initialState, 
    applyMiddleware(thunk),
    ... ...
);

简单来说,createStore做了这么件事:
目的:根据你传入的reducer和初始状态initialState生成初始化store,并提供了一些列操作的接口,像dispatch等
怎么做的呢?参考Redux-creatStore/compose
本文重点讲解中间件的执行过程和原理

3、中间件运行原理

中间件的执行原理和koa中间件的执行原理类似,但是不是洋葱型的,而是半个洋葱,因为redux是单向执行的,走过去就完事了。
当你应用了中间件,在触发一个action操作的时候,action操作就会经过先经过中间件,最终再形成dispatch(action)。
以其中两个中间件为例,说明下,一个触发一个action动作的时候,代码的执行逻辑。
thunk:是允许dispatch一个函数,而不是一个对象
假如说异步打印一个日志。

3.1 中间件的内部逻辑
const store = createStore(reducer, preloadedState, enchancer);

// 如果没有中间件,正常触发一个action;
// 如果有中间件的时候 creatStore内部的执行逻辑是这样的
// enchancer 就是你应用的中间件,调用applyMiddleware得到的组合中间件

function applyMiddleware(...middlewares) {
  return createStore => (...args) => {
  
    // 该创建的store还是要创建的,只传入了两个参数,没有中间件,得到的是正常的store
    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.`
      )
    }
    // 把getState、dispatch传给中间件
    const middlewareAPI = {
      getState: store.getState,
      dispatch: (...args) => dispatch(...args)
    }
    // 下面方法返回来了一个函数数组,中间件被剥离到
    // next => {}
    const chain = middlewares.map(middleware => middleware(middlewareAPI))
    // 再执行下面的,中间件就被剥离到
    // action => {}
    dispatch = compose(...chain)(store.dispatch)

    return {
      ...store,
      dispatch
    }
  }
}

// 下面得到的结果是 
// 假设中间件为 a b
// a(b(store.dispatch))
return enchancer(createStore)(reducer, preloadedState);

// 结合上面thunk的源码
({ dispatch, getState }) => next => action => {
    if (typeof action === "function") {
        return action(dispatch, getState);
    }
    return next(action);
  };

经过上面的操作后,经过中间件包装后的store是什么样子
假设中间件为 a b c
没有中间件的时候是这样的

store = {
    dispatch,
    ... ...,
    subscribe,
    getState
}

经过中间包装后,store变成了

store = {
    dispatch: a(b((store.dispatch))),
    ... ...,
    subscribe,
    getState
}

总结起来就是给每一个中间件配发了一个原始store的dispatch,中间件函数嵌套执行

3.2 触发一个action时,执行逻辑

假设触发一个异步打印日志的功能

应用中间件

const store = createStore(rootReducer, initialState, 
    applyMiddleware(thunk)
);

经过上面的操作,现在的store应该是

{
    dispatch: action => {
        if (typeof action === "function") {
            return action(dispatch, getState, extraArgument);
        }
        return next(action);
    },
    ... ...,
    subscribe,
    getState
}

action函数

当执行这个logNext的时候,返回一个函数,函数的参数是dispatch和getState两个。

const logNext = () => (dispatch, getState) => {
    setTimeout(
        dispatch({
            type: "LOG",
            payload: {
                content: "这是一条异步日志"
            }})
        ,5000);
}

执行过程

---->
store.dispatch(logNext())  // 传了一个函数,然后执行一个函数
----> 
(dispatch, getState) => {
    setTimeout(
        dispatch({
            type: "LOG",
            payload: {
                content: "这是一条异步日志"
            }})
        ,5000);
} 
---->

可以看出来,redux-thunk就是一个封装函数,允许store.dispatch一个函数
如果有多个中间件,执行过程是什么样子的?重点在next(action),next是什么呢?
next就是每一个中间件要做的事情

next => action => {}

明白了么?

附录 compsoe
// compose本身并不改变函数的执行,将函数组合后又返回了一个函数
    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)))
    }

文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。

转载请注明本文地址:https://www.ucloud.cn/yun/98334.html

相关文章

  • redux-saga框架使用详解及Demo教程

    摘要:通过创建将所有的异步操作逻辑收集在一个地方集中处理,可以用来代替中间件。 redux-saga框架使用详解及Demo教程 前面我们讲解过redux框架和dva框架的基本使用,因为dva框架中effects模块设计到了redux-saga中的知识点,可能有的同学们会用dva框架,但是对redux-saga又不是很熟悉,今天我们就来简单的讲解下saga框架的主要API和如何配合redux框...

    Nosee 评论0 收藏0
  • 详解react、redux、react-redux之间的关系

    摘要:或者兄弟组件之间想要共享某些数据,也不是很方便传递获取等。后面要讲到的就是通过让各个子组件拿到中的数据的。所以,确实和没有什么本质关系,可以结合其他库正常使用。 本文介绍了react、redux、react-redux之间的关系,分享给大家,也给自己留个笔记,具体如下: React 一些小型项目,只使用 React 完全够用了,数据管理使用props、state即可,那什么时候需要引入...

    xioqua 评论0 收藏0
  • redux middleware 详解

    摘要:执行完后,获得数组,,它保存的对象是图中绿色箭头指向的匿名函数,因为闭包,每个匿名函数都可以访问相同的,即。是函数式编程中的组合,将中的所有匿名函数,,组装成一个新的函数,即新的,当新执行时,,从左到右依次执行所以顺序很重要。 前言 It provides a third-party extension point between dispatching anaction, and t...

    yanwei 评论0 收藏0

发表评论

0条评论

Shihira

|高级讲师

TA的文章

阅读更多
最新活动
阅读需要支付1元查看
<