redux源码图解:createStore 和 applyMiddleware
在研究 redux-saga时,发现自己对 redux middleware 不是太了解,因此,便决定先深入解读一下 redux 源码。跟大多数人一样,发现 redux源码 真的很精简,目录结构如下:
|—— utils
    |—— warnings.js
|—— applyMiddleware.js
|—— bindActionCreator.js
|—— combineReducers.js
|—— compose.js
|—— createStore.js
|—— index.js
在 index.js 中导出了5个模块,即外部可用的:
export {
  createStore,
  combineReducers,
  bindActionCreators,
  applyMiddleware,
  compose
}
然而,当真正解读的时候,发现还真是有点吃不消,经过几天的硬啃之后,只能说:终于等到你,还好我没放弃。。。(自带BGM)
这里,我可能不会仔细去分析它的源码,但会就自己的理解进行梳理概括,具体的对我很有帮助的文章会放到结尾参考处。
首先是对 createStore 、 applyMiddleware 及 compose 的梳理,因为这3个模块存在相互调用的关系,其关系图如下(高清图请查看 redux源码图解之createStore和applyMiddleware):

1. 每个模块的作用
createStore 的函数的作用就是生成一个 store 对象,这个对象具有5个方法:
return {
    dispatch,  // 传入 action,调用 reducer 及触发 subscribe 绑定的监听函数
    subscribe,
    getState,
    replaceReducer,  // 用新的 reducer 代替当前的 reducer,使用不多
    [$$observable]: observable
  }
而 applyMiddleware 函数的作用就是对 store.dispatch 方法进行增强和改造,使得在发出 Action 和执行 Reducer 之间添加其他功能。
compose 函数则是 applyMiddleware 函数的核心,其会形成串联的函数调用关系,用于增强 dispatch 方法。
2. 模块之间的调用关系
(i) 首先,createStore 模块会对传入的参数进行判断,分别处理不同参数的情况,当传入 applyMiddleware(...middleware) 的时候,就会返回 applyMiddleware(...middleware) 执行之后的高阶函数,即:
// createStore.js
if (typeof enhancer !== 'undefined') {
    if (typeof enhancer !== 'function') {
      throw new Error('Expected the enhancer to be a function.')
    }
    // 这里的 enhancer 是 applyMiddleware(...) 执行后的高阶函数,传参无 enhancer
    return enhancer(createStore)(reducer, preloadedState)
  }
(ii)然后,就进入了 applyMiddleware 模块内部的逻辑,从 createStore 返回的高阶函数,其传入的参数是没有 enhancer 的,因此走的是 createStore 函数中没有传入 enhancer 的逻辑,用于先获得没有中间件时返回的 store。
// applyMiddleware.js
export default function applyMiddleware(...middlewares) {
  return (createStore) => (reducer, preloadedState, enhancer) => {
    const store = createStore(reducer, preloadedState, enhancer)
    let dispatch = store.dispatch
    let chain = []  // 用于存放获取了store的中间件数组
    const middlewareAPI = {
      getState: store.getState,
      dispatch: (...args) => dispatch(...args)
    }
    chain = middlewares.map(middleware => middleware(middlewareAPI))
    dispatch = compose(...chain)(store.dispatch)
    return {
      ...store,
      dispatch
    }
  }
}
(iii)接着,获取最原始的 store 的 getState 和 dispatch,封装于 middlewareAPI 对象。将该对象传入中间件 middleware 中,形成中间件数组 chain。
其中,middleware 的范式是:(store) => (next) => (action) => {},将 middlewareAPI 传入middleware 后,中间件便获得了 {getState, dispacth}。至此,chain 中间件数组中包含的每个中间件的形式都变成了 (next) => (action) => {} 的形式。
(iiii)最后,调用 compose 函数,如 chian = [middleware1, middleware2, middleware3],则 dispatch = compose(...chain)(store.dispatch),即执行 middleware1(middleware2(middleware3(store.dispatch))),然后赋值给 dispatch。
总之,不管是否有
applyMiddleware,createStore的结果都是输出一个 store 对象,而applyMiddleware则可以对 store 对象中的dispatch进行改造。
3. 参考
- redux middleware 详解
 - Redux源码分析(一):主要架构,createStore.js,applyMiddleware.js和compose.js部分
 - Redux 入门教程(二):中间件与异步操作
 
redux源码图解:createStore 和 applyMiddleware的更多相关文章
- redux源码阅读之compose,applyMiddleware
		
我的观点是,看别人的源码,不追求一定要能原样造轮子,单纯就是学习知识,对于程序员的提高就足够了.在阅读redux的compose源码之前,我们先学一些前置的知识. redux源码阅读之compose, ...
 - Redux 源码解读--createStore,js
		
一.依赖:$$observable.ActionTypes.isPlainObject 二.接下来看到直接 export default 一个 createStore 函数,下面根据代码以及注释来分析 ...
 - Redux源码分析之createStore
		
接着前面的,我们继续,打开createStore.js, 直接看最后, createStore返回的就是一个带着5个方法的对象. return { dispatch, subscribe, getSt ...
 - Redux源码分析之applyMiddleware
		
Redux源码分析之基本概念 Redux源码分析之createStore Redux源码分析之bindActionCreators Redux源码分析之combineReducers Redux源码分 ...
 - redux源码学习笔记 - createStore
		
本篇是学习redux源码的一些记录,学习的redux版本是^4.0.1. 在页面开发时,需要管理很多状态(state),比如服务器响应,缓存数据,UI状态等等···当页面的庞大时,状态就会变的混乱.r ...
 - Redux源码分析之基本概念
		
Redux源码分析之基本概念 Redux源码分析之createStore Redux源码分析之bindActionCreators Redux源码分析之combineReducers Redux源码分 ...
 - Redux源码分析之bindActionCreators
		
Redux源码分析之基本概念 Redux源码分析之createStore Redux源码分析之bindActionCreators Redux源码分析之combineReducers Redux源码分 ...
 - Redux源码分析之combineReducers
		
Redux源码分析之基本概念 Redux源码分析之createStore Redux源码分析之bindActionCreators Redux源码分析之combineReducers Redux源码分 ...
 - Redux源码分析之compose
		
Redux源码分析之基本概念 Redux源码分析之createStore Redux源码分析之bindActionCreators Redux源码分析之combineReducers Redux源码分 ...
 
随机推荐
- JAVA 把小数分成整数和小数
			
在进行进制转换的时候,我们需要把小数分为整数和小数两部分. 这里介绍两种方法. 第一种举个例子:1.23分为1 和 0.23 第二种:1.23 分为 1 和23 有时需要具体情况具体分析自己需要哪种类 ...
 - hello2 源码分析
			
1.GreetingServlet.java(问候页面): /** * Copyright (c) 2014 Oracle and/or its affiliates. All rights rese ...
 - Qt 比对TreeItem节点
			
void TreeModel::settingsUpdate(const QStringList &lines){ QList<TreeItem*> parents; TreeIt ...
 - ps切图
			
1.选择视图然后标尺 ctrl+R 拉辅助线,选择矩形选框工具,点击窗口,选择信息,在右上角点击面板选项,选择像素,获取宽高(width 横向为宽:height纵向为高) 2.视图:清楚参考线 Ctr ...
 - unity 中让Text的文字动态刷新形式
			
第一种刷新文字形式 using UnityEngine; using System.Collections; using UnityEngine.UI; public class SensorText ...
 - CodeForces - 1040B Shashlik Cooking(水题)
			
题目: B. Shashlik Cooking time limit per test 1 second memory limit per test 512 megabytes input stand ...
 - left join on 后and 和 where 的区别
			
SELECT * FROM student a LEFT JOIN sc b ON a.Sid = b.Sid AND a.Sname="赵雷" 结果:(left join 左连接 ...
 - python中的unique()
			
a = np.unique(A) 对于一维数组或者列表,unique函数去除其中重复的元素,并按元素由大到小返回一个新的无元素重复的元组或者列表 import numpy as np A = [1, ...
 - table-layout:fixed;   表格比例固定
			
固定表格布局: 固定表格布局与自动表格布局相比,允许浏览器更快地对表格进行布局. 在固定表格布局中,水平布局仅取决于表格宽度.列宽度.表格边框宽度.单元格间距,而与单元格的内容无关. 通过使用固定表格 ...
 - 编译libcurl支持https协议
			
编译与安装参考:http://www.cnblogs.com/openiris/p/3812443.html 注意事项:先下载安装完nasm和perl再打开控制台(需要将nasm安装路径添加到Path ...