十分钟理解Redux中间件

栏目: IOS · Android · 发布时间: 5年前

内容简介:由于一直用业界封装好的如首先简单提下什么是中间件,该部分与下文关系不大,可以跳过。来看眼这个经典的图。

由于一直用业界封装好的如 redux-logger、redux-thunk 此类的中间件,并没有深入去了解过 redux 中间件的实现方式。正好前些时间有个需求需要对 action 执行时做一些封装,于是借此了解了下 Redux Middleware 的原理。

* 中间件概念

首先简单提下什么是中间件,该部分与下文关系不大,可以跳过。来看眼这个经典的图。

十分钟理解Redux中间件

不难发现:

  1. 不使用 middleware 时,在 dispatch(action) 时会执行 rootReducer ,并根据 actiontype 更新返回相应的 state
  2. 而在使用 middleware 时,简言之, middleware 会将我们当前的 action 做相应的处理,随后再交付 rootReducer 执行。

简单实现原理

比如现有一个 action 如下:

function getData() {
  return {
      api: '/cgi/getData',
      type: [GET_DATA, GET_DATA_SUCCESS, GET_DATA_FAIL]
  }
}

我们希望执行该 action 时可以发起相应请求,并且根据请求结果由定义的 type 匹配到相应的 reducer ,那么可以自定义中间件处理该 action ,因此该方法封装成中间件之前可能是这样的:

function dispatchPre(action, dispatch) {
    const api = action.api;
    const [ fetching_type, success_type,  fail_type] = action.type;
    // 拉取数据
    const res = await request(api);
    
    // 拉取时状态
    dispatch({type: fetching_type});
    // 成功时状态
    if (res.success) {
        dispatch({type: success_type, data: res.data});
        console.log('GET_SUCCESS');
    }
    // 失败时状态
    if (res.fail) {
        dispatch({type: fail_type});
        console.log('GET_FAIL');
    };
}

// 调用: dispatchPre(action())

那如何封装成中间件,让我们在可以直接在 dispatch(action) 时就做到这样呢?可能会首先想到改变 dispatch 指向

// 储存原来的dispatch
const dispatch = store.dispatch;
// 改变dispatch指向
store.dispatch = dispatchPre;
// 重命名
const next = dispatch;

截止到这我们已经了解了中间件的基本原理了~

源码分析

了解了基本原理能有助于我们更快地读懂 middleware 的源码。一般我们会这样添加中间件并使用。

createStore(rootReducer, applyMiddleware.apply(null, [...middlewares]))

接下来我们可以重点关注这两个函数 createStoreapplyMiddleware

CreateStore

// 摘至createStore
export function createStore(reducer, rootState, enhance) {
    ...
    if (typeof enhancer !== 'undefined') {
        if (typeof enhancer !== 'function') {
          throw new Error('Expected the enhancer to be a function.')
        }
    /*
        若使用中间件,这里 enhancer 即为 applyMiddleware()
        若有enhance,直接返回一个增强的store对象
    */
    return enhancer(createStore)(reducer, preloadedState)
  }
  ...
}

ApplyMiddleware

再看看 applyMiddleware 做了什么, applyMiddleware 函数非常简单,就十来行代码,这里将其完整复制出来。

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.`
      )
    }

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

    return {
      ...store,
      dispatch
    }
  }
}

执行步骤

可以将其主要功能按步骤划分如下:

1、依次执行 middleware

middleware 执行后返回的函数合并到一个 chain 数组,这里我们有必要看看标准 middleware 的定义格式,如下

export default store => next => action => {}

// 即
function (store) {
    return function(next) {
        return function (action) {
            return {}
        }
    }
}

那么此时合并的 chain 结构如下

[    ...,
    function(next) {
        return function (action) {
            return {}
        }
    }
]

2、改变 dispatch 指向。

想必你也注意到了 compose 函数, compose 函数如下:

[...chain].reduce((a, b) => (...args) => a(b(...args)))

实际就是一个柯里化函数,即将所有的 middleware 合并成一个 middleware ,并在最后一个 middleware 中传入当前的 dispatch 。这里再使用一个简单的例子方便大家理解。

// 假设chain如下:
chain = [
    a: next => action => { console.log('第1层中间件') return next(action) }
    b: next => action => { console.log('第2层中间件') return next(action) }
    c: next => action => { console.log('根dispatch') return next(action) }
]

可以发现已经将所有 middleware 串联起来了,于是我在之后执行 dispatch 时,就是一个合并了所有中间件的 dispatchFun

最后看一下这时候compose执行返回,如下

dispatch = a(b(c(dispatch)))

// 调用dispatch(action)
// 执行循序
/*
   1. 调用 a(b(c(dispatch)))(action) __print__: 第1层中间件
   2. 返回 a: next(action) 即b(c(dispatch))(action)
   3. 调用 b(c(dispatch))(action) __print__: 第2层中间件
   4. 返回 b: next(action) 即c(dispatch)(action)
   5. 调用 c(dispatch)(action) __print__: 根dispatch
   6. 返回 c: next(action) 即dispatch(action)
   7. 调用 dispatch(action)
*/

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持 码农网

查看所有标签

猜你喜欢:

本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们

Java编程思想

Java编程思想

[美] Bruce Eckel / 陈昊鹏、饶若楠 / 机械工业出版社 / 2005-9 / 95.00元

本书赢得了全球程序员的广泛赞誉,即使是最晦涩的概念,在Bruce Eckel的文字亲和力和小而直接的编程示例面前也会化解于无形。从Java的基础语法到最高级特性(深入的面向对象概念、多线程、自动项目构建、单元测试和调试等),本书都能逐步指导你轻松掌握。 从本书获得的各项大奖以及来自世界各地的读者评论中,不难看出这是一本经典之作。本书的作者拥有多年教学经验,对C、C++以及Java语言都有独到......一起来看看 《Java编程思想》 这本书的介绍吧!

JS 压缩/解压工具
JS 压缩/解压工具

在线压缩/解压 JS 代码

RGB CMYK 转换工具
RGB CMYK 转换工具

RGB CMYK 互转工具

HEX CMYK 转换工具
HEX CMYK 转换工具

HEX CMYK 互转工具