在研究 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)

这里,我可能不会仔细去分析它的源码,但会就自己的理解进行梳理概括,具体的对我很有帮助的文章会放到结尾参考处。

首先是对 createStoreapplyMiddlewarecompose 的梳理,因为这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 的 getStatedispatch,封装于 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

总之,不管是否有 applyMiddlewarecreateStore 的结果都是输出一个 store 对象,而 applyMiddleware 则可以对 store 对象中的 dispatch 进行改造。

3. 参考

redux源码图解:createStore 和 applyMiddleware的更多相关文章

  1. redux源码阅读之compose,applyMiddleware

    我的观点是,看别人的源码,不追求一定要能原样造轮子,单纯就是学习知识,对于程序员的提高就足够了.在阅读redux的compose源码之前,我们先学一些前置的知识. redux源码阅读之compose, ...

  2. Redux 源码解读--createStore,js

    一.依赖:$$observable.ActionTypes.isPlainObject 二.接下来看到直接 export default 一个 createStore 函数,下面根据代码以及注释来分析 ...

  3. Redux源码分析之createStore

    接着前面的,我们继续,打开createStore.js, 直接看最后, createStore返回的就是一个带着5个方法的对象. return { dispatch, subscribe, getSt ...

  4. Redux源码分析之applyMiddleware

    Redux源码分析之基本概念 Redux源码分析之createStore Redux源码分析之bindActionCreators Redux源码分析之combineReducers Redux源码分 ...

  5. redux源码学习笔记 - createStore

    本篇是学习redux源码的一些记录,学习的redux版本是^4.0.1. 在页面开发时,需要管理很多状态(state),比如服务器响应,缓存数据,UI状态等等···当页面的庞大时,状态就会变的混乱.r ...

  6. Redux源码分析之基本概念

    Redux源码分析之基本概念 Redux源码分析之createStore Redux源码分析之bindActionCreators Redux源码分析之combineReducers Redux源码分 ...

  7. Redux源码分析之bindActionCreators

    Redux源码分析之基本概念 Redux源码分析之createStore Redux源码分析之bindActionCreators Redux源码分析之combineReducers Redux源码分 ...

  8. Redux源码分析之combineReducers

    Redux源码分析之基本概念 Redux源码分析之createStore Redux源码分析之bindActionCreators Redux源码分析之combineReducers Redux源码分 ...

  9. Redux源码分析之compose

    Redux源码分析之基本概念 Redux源码分析之createStore Redux源码分析之bindActionCreators Redux源码分析之combineReducers Redux源码分 ...

随机推荐

  1. JAVA 把小数分成整数和小数

    在进行进制转换的时候,我们需要把小数分为整数和小数两部分. 这里介绍两种方法. 第一种举个例子:1.23分为1 和 0.23 第二种:1.23 分为 1 和23 有时需要具体情况具体分析自己需要哪种类 ...

  2. hello2 源码分析

    1.GreetingServlet.java(问候页面): /** * Copyright (c) 2014 Oracle and/or its affiliates. All rights rese ...

  3. Qt 比对TreeItem节点

    void TreeModel::settingsUpdate(const QStringList &lines){ QList<TreeItem*> parents; TreeIt ...

  4. ps切图

    1.选择视图然后标尺 ctrl+R 拉辅助线,选择矩形选框工具,点击窗口,选择信息,在右上角点击面板选项,选择像素,获取宽高(width 横向为宽:height纵向为高) 2.视图:清楚参考线 Ctr ...

  5. unity 中让Text的文字动态刷新形式

    第一种刷新文字形式 using UnityEngine; using System.Collections; using UnityEngine.UI; public class SensorText ...

  6. CodeForces - 1040B Shashlik Cooking(水题)

    题目: B. Shashlik Cooking time limit per test 1 second memory limit per test 512 megabytes input stand ...

  7. left join on 后and 和 where 的区别

    SELECT * FROM student a LEFT JOIN sc b ON a.Sid = b.Sid AND a.Sname="赵雷" 结果:(left join 左连接 ...

  8. python中的unique()

    a = np.unique(A) 对于一维数组或者列表,unique函数去除其中重复的元素,并按元素由大到小返回一个新的无元素重复的元组或者列表 import numpy as np A = [1, ...

  9. table-layout:fixed; 表格比例固定

    固定表格布局: 固定表格布局与自动表格布局相比,允许浏览器更快地对表格进行布局. 在固定表格布局中,水平布局仅取决于表格宽度.列宽度.表格边框宽度.单元格间距,而与单元格的内容无关. 通过使用固定表格 ...

  10. 编译libcurl支持https协议

    编译与安装参考:http://www.cnblogs.com/openiris/p/3812443.html 注意事项:先下载安装完nasm和perl再打开控制台(需要将nasm安装路径添加到Path ...