redux之所以伟大就在于中间件了,中间件为redux提供了无限可能。redux中中间件是一个不太容易理解的概念,因为涉及到compose、hoc等函数式的概念,看源代码总是懵懵的感觉。今天我们就来详细解剖一下伟大的applyMiddleware吧。

applyMiddleware只有短短三十多行,可见作者功力。先简单说下中间件是啥,在redux中,当你要dispatch一条命令给reducer时,预先定义的中间件会对这条命令进行各种转换,有的中间件会记录这个过程,有的会对payload进行转换,甚至可以终止这次dispatch(短路),再发起另一个命令。下面我们切入正题。

入参

export default applyMiddleware(...middlewares) {
...
}

首先通过rest参数的方式获取一个中间件数组。

返回值

export default applyMiddleware(...middlewares) {
return (createStore) => (reducer, preloadedState, enhancer) => { return {
...store,
dispatch
}
}
}

可以看到返回值是一个HOC,它接受一个createStore函数,因此你还可以使用applyMiddleware来创建store

const finalCreateStore = applyMiddleware(thunk, log)(createStore)
const store = finalCreateStore(reducer, preloadedState, enhancer)

重点来了

先把代码贴上,

const store = createStore(reducer, preloadedState, enhancer)
let dispatch = store.dispatch
let chain = [] const middlewareAPI = {
getState: store.getState,
dispatch: (action) => dispatch(action)
}
chain = middlewares.map(middleware => middleware(middlewareAPI))
dispatch = compose(...chain)(store.dispatch)

这里不对compose做过多解释了,请大家自行了解。

我们直接锁定到chain = middlewares.map(middleware => middleware(middlewareAPI)),这里每个middleware接受middlewareAPI作为参数,那么我们知道了middleware大概的样子即,

const middleware = function({getState : function, dispatch: function}) {
...
}

我们继续,下一行代码就是精髓了,dispatch = compose(...chain)(store.dispatch),首先compose(...chain)不难理解,即从右到左chain中的每个middleware(注意这里的middleware并不是一开始传进来的middleware了,是一个函数执行的结果)所执行的结果将作为左边middleware的入参,后面以此类推,那么store.dispatch理所当然成为chain中最后一个middleware的入参,那么我们知道最后一个middleware应该是这样的。

const middleware = function({getState : function, dispatch: function}) {
return (store.dispatch) {
...
}
}

这里我们换一个更加普适的写法,

const middleware = function({getState : function, dispatch: function}) {
return (next) => {
...
}
}

难点来了,到这里显而易见的线索就不多了,由代码可以看出,最终这个compose后的dispatch被return出去了,我们在实际开发中,比如dispatch({type: 'INCREMENT'}),这里的dispatch并不是store.dispatch(记为realDispatch),而是return的那个dispatch(我们记为fakeDispatch),因此又多了不少线索,我们fakeDispatch的action最终要传给realDispatch来触发reducer中的逻辑,那么我们如何传递action呢? 我们回到我们现在猜测的middleware代码中,最后一个middleware的next就是realDispatch,现在我们把注意力放在这里,即应该要给左边的middleware返回什么呢,

// 这是middleware的执行结果
const middleare = (next) => {
// 这里要返回什么呢
}

如果我们返回一个非函数,那么这个middleware以后就没啥事了,显然不行,因为我还需要接受action,再调用realDispatch;所以我们需要返回一个闭包函数,这个函数保留了next的访问引用,那么我们继续猜测最后一个middleware,

const middleware = (next) => {
return (action) => {
next(action)
}
}

OK,现在我们可以保证其可以将action顺利送给reducer了,回顾下compose,我们可以看到,(action) => {//...} 就是每个函数的返回值,它会作为入参传给左侧函数,这个入参即next,这样action从fakeDispatch出发,经过这样一个个action作为参数的next函数,最终到达realDispatch这个next并触发reducer执行。下面我们来个完整的middleware,

const middleware = (getState, dispatch) => (next) => (action) => { // 具体逻辑...}

如果你对redux-logger和redux-thunk的源码有些记忆的话你会很熟悉这个函数签名了吧。原来这个难懂的HOC就是这么来的。这你也就知道了redux-thunk为何要放到最左边,因为它要能够短路,即action从fakeDispatch传进来时,它是第一个接收的,这样它就可以根据anction类型来确定是否终止这个dispatch,下面我们写下简版redux-logger和redux-thunk,实战下。

redux-thunk

const middleware = (getState, dispatch) => (next) => (action) => {
if(typeof action === 'function') {
action(getState, dispatch) // 短路
} else {
next(action)
}
}

redux-logger

const middleware = (getState, dispatch) => (next) => (action) => {
console.log('开始触发reducer')
next(action)
console.log('结束触发reducer')
}

redux之applyMiddleware的更多相关文章

  1. redux 中间件 --- applyMiddleware 源码解析 + 中间件的实战

    前传  中间件的由来 redux的操作的过程,用户操作的时候,我们通过dispatch分发一个action,纯函数reducer检测到该操作,并根据action的type属性,进行相应的运算,返回st ...

  2. react+redux教程(七)自定义redux中间件

    今天,我们要讲解的是自定义redux中间件这个知识点.本节内容非常抽象,特别是中间件的定义原理,那多层的函数嵌套和串联,需要极强逻辑思维能力才能完全消化吸收.不过我会多罗嗦几句,所以不用担心. 例子 ...

  3. redux middleware 的理解

    前言 这几天看了redux middleware的运用与实现原理,写了一个百度搜索的demo,实现了类似redux-thunk和redux-logger中间件的功能. 项目地址:https://git ...

  4. Flux --> Redux --> Redux React 入门

    本文的目的很简单,介绍Redux相关概念用法 及其在React项目中的基本使用 假设你会一些ES6.会一些React.有看过Redux相关的文章,这篇入门小文应该能帮助你理一下相关的知识 一般来说,推 ...

  5. Redux学习(2) ----- 异步和中间件

    Redux中间件,其实就是一个函数, 当我们发送一个action的时候,先经过它,我们就可以对action进行处理,然后再发送action到达reducer, 改变状态,这时我们就可以在中间件中,对a ...

  6. redux源码解析-函数式编程

    提到redux,会想到函数式编程.什么是函数式编程?是一种很奇妙的函数式的编程方法.你会感觉函数式编程这么简单,但是用起来却很方便很神奇. 在<functional javascript> ...

  7. redux的源码解析

    一. redux出现的动机 1. Javascript 需要管理比任何时候都要多的state2. state 在什么时候,由于什么原因,如何变化已然不受控制.3. 来自前端开发领域的新需求4. 我们总 ...

  8. Flux --> Redux --> Redux React 基础实例教程

    本文的目的很简单,介绍Redux相关概念用法 及其在React项目中的基本使用 假设你会一些ES6.会一些React.有看过Redux相关的文章,这篇入门小文应该能帮助你理一下相关的知识 一般来说,推 ...

  9. 再探Redux Middleware

    前言 在初步了解Redux中间件演变过程之后,继续研究Redux如何将中间件结合.上次将中间件与redux硬结合在一起确实有些难看,现在就一起看看Redux如何加持中间件. 中间件执行过程 希望借助图 ...

随机推荐

  1. java script简介

    JavaScript一种直译式脚本语言,是一种动态类型.弱类型.基于原型的语言,内置支持类型.它的解释器被称为JavaScript引擎,为浏览器的一部分,广泛用于客户端的脚本语言,最早是在HTML(标 ...

  2. sql server数据库文件的迁移(mdf&ldf文件)

    mdf 源文件 ldf 日志文件 在服务器不同磁盘目录内做迁移.移动用户数据库文件的情况大致有下面一些: ① 没有设计规划好,贪图方便,,,,数据库文件和日志文件增长过快,导致存放数据库文件的磁盘空间 ...

  3. mysqli链接数据库

    <?php $uid = $_GET['uid']; $host = 'localhost';$database = 'test';$username = 'root';$password = ...

  4. mysql修改联合主键

    参考 https://blog.csdn.net/BockSong/article/details/80933477 alter table TABNAME drop primary key; alt ...

  5. docker+efk+.net core部署

    部署环境 centos7 本主要利用efk实现日志收集 一.创建docker-compose es地址:https://www.elastic.co/guide/en/elasticsearch/re ...

  6. Ubuntu开机时提示“piix4_smbus 0000:00:07.3: SMBus Host controller not enabled”

    问题描述:Ubuntu开机时提示“piix4_smbus 0000:00:07.3: SMBus Host controller not enabled” 版本:Ubuntu 18.04    VMw ...

  7. mysql学习笔记--数据库多表查询

    一.内连接[inner join] 1. 语法一:select 列名 from 表1 inner join 表2 on 表1.公共字段=表2.公共字段 2. 语法二:select 列名 from 表1 ...

  8. 贝叶斯分类器,随机森林,梯度下载森林,神经网络相关参数的意义和data leakage

    构建的每一颗树的数据都是有放回的随机抽取的(也叫bootstrap),n_estimators参数是你想设置多少颗树,还有就是在进行树的结点分裂的时候,是随机选取一个特征子集,然后找到最佳的分裂标准.

  9. Hadoop环境搭建--Docker完全分布式部署Hadoop环境(菜鸟采坑吐血整理)

    系统:Centos 7,内核版本3.10 本文介绍如何从0利用Docker搭建Hadoop环境,制作的镜像文件已经分享,也可以直接使用制作好的镜像文件. 一.宿主机准备工作 0.宿主机(Centos7 ...

  10. AD16PCB如何快速删除走线

    工具(Tools)取消布线(Un_Route)全部(AII) ad pcb画图,如果想整体去掉一条线,只要是连接在一起的,不管在哪一层,都可以采取如下方法:1.PCB画面下,按组合键Ctrl+H,会出 ...