博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
十分钟理解Redux中间件
阅读量:6212 次
发布时间:2019-06-21

本文共 4525 字,大约阅读时间需要 15 分钟。

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

* 中间件概念

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

clipboard.png

不难发现:

  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,因此该方法封装成中间件之前可能是这样的:

async 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)

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

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

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

源码分析

了解了基本原理能有助于我们更快地读懂middleware的源码。

业务中,一般我们会这样添加中间件并使用。

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

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

CreateStore

// 摘至createStoreexport 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,直接返回一个增强的createStore方法,可以类比成react的高阶函数    */    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)    }    // 1、将store对象的基本方法传递给中间件并依次调用中间件    const chain = middlewares.map(middleware => middleware(middlewareAPI))    // 2、改变dispatch指向,并将最初的dispatch传递给compose    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

*compose可能会看得有点蒙,不理解柯里化函数的同学可以跳到先了解下。

// 假设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) }]

调用compose(...chain)(store.dispatch)后返回a(b(c(dispatch)))

可以发现已经将所有middleware串联起来了,并同时修改了dispatch的指向。
最后看一下这时候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)*/

*一个例子读懂compose

上文提到compose是个柯里化函数,可以看成是将所有函数合并成一个函数并返回的函数。

例如先定义3个方法

function A(x){    return x + 'a'}function B(y){    return y + 'b'}function C(){    return 'c'}var d = [...A, b, C].reduce((a, b) => (d) => {console.log(d, a, b); a(b(d))})d // 打印d// f (d) { console.log(d, a, b); return a(b(d)) }d('d') // 调用d/* * d * f(d) { console.log(d, a, b); return a(b(d)) } * f C() { return 'c' }*//* * c * f A(x) { return x + 'a' } * f B(y) { return y + 'b' }*/

不难发现,使用闭包,在调用d的时候,将ab函数储存在了内存中,调用时会依次将数组从右至左的函数返回做为参数传递给下一个函数使用

转载地址:http://stdja.baihongyu.com/

你可能感兴趣的文章
CSS 定位之 z-index 问题分析
查看>>
HDOJ 2023 求平均成绩
查看>>
将usb 声卡集成到android4.0上
查看>>
Netty那点事(二)Netty中的buffer
查看>>
Magento通过命令行刷新索引(index)
查看>>
linux自学_shell理论基础
查看>>
Hive编译及部署
查看>>
从输入 URL 到页面加载完的过程中都发生了什么事情 —— 网络优化篇
查看>>
域控制器上安装杀毒软件
查看>>
关于v8的编译
查看>>
为Bash和VIM配置一个美观奢华的状态提示栏
查看>>
linux下tar.gz、tar、bz2、zip等解压缩、压缩命令小结
查看>>
Hive SemanticException:Expression not in GROUP BY
查看>>
oracle中delete drop truncate的用法和区别
查看>>
走出一条光明路--1远远大于0
查看>>
mysql 5.5安装
查看>>
ntpdate 的问题
查看>>
Java 多态性理解
查看>>
安装Discuz
查看>>
GDPR如何影响中国企业?
查看>>