写在前面

redux的源码很简洁,除了applyMiddleware比较绕难以理解外,大部分还是

这里假设读者对redux有一定了解,就不科普redux的概念和API啥的啦,这部分建议直接看官方文档

此外,源码解析的中文批注版已上传至github,可点击查看。本文相关示例代码,可点击查看

源码解析概览

将redux下载下来,然后看下他的目录结构。

npm install redux

这里我们需要关心的主要是src目录,源码解析需要关心的文件都在这里面了

  • index.js:redux主文件,主要对外暴露了几个核心API

  • createStore.jscreateStore 方法的定义

  • utils:各种工具方法,其中applyMiddleware、combineReducers、bindActionCreators 为redux的几个核心方法,剩余的pick、mapValue、compose为普通的工具函数

➜  src git:(master) ✗ tree
.
├── createStore.js
├── index.js
└── utils
├── applyMiddleware.js
├── bindActionCreators.js
├── combineReducers.js
├── compose.js
├── isPlainObject.js
├── mapValues.js
└── pick.js

源码解析:index.js

超级简单,暴露了几个核心API,没了

mport createStore from './createStore';
import combineReducers from './utils/combineReducers';
import bindActionCreators from './utils/bindActionCreators';
import applyMiddleware from './utils/applyMiddleware';
import compose from './utils/compose'; export {
createStore,
combineReducers,
bindActionCreators,
applyMiddleware,
compose
};

源码解析:createStore.js

直接贴上源代码,并进行简单注解。看下redux.createStore(reducer, initialState)调用的文档说明,基本就能够看懂下面代码了。

特别强调:虽然在几个文件里,createStore.js的代码行数是最多的,但却是最容易读懂的。下面几点比较关键

  1. redux.createStore(reducer, initialState) 传入了reducer、initialState,并返回一个store对象。

  2. store对象对外暴露了dispatch、getState、subscribe方法

  3. store对象通过getState() 获取内部状态

  4. initialState为 store 的初始状态,如果不传则为undefined

  5. store对象通过reducer来修改内部状态

  6. store对象创建的时候,内部会主动调用dispatch({ type: ActionTypes.INIT });来对内部状态进行初始化。通过断点或者日志打印就可以看到,store对象创建的同时,reducer就会被调用进行初始化。

import isPlainObject from './utils/isPlainObject';

/**
* These are private action types reserved by Redux.
* For any unknown actions, you must return the current state.
* If the current state is undefined, you must return the initial state.
* Do not reference these action types directly in your code.
*/
// 初始化的时候(redux.createStore(reducer, initialState)时),传的action.type 就是这货啦
export var ActionTypes = {
INIT: '@@redux/INIT'
}; /**
* Creates a Redux store that holds the state tree.
* The only way to change the data in the store is to call `dispatch()` on it.
*
* There should only be a single store in your app. To specify how different
* parts of the state tree respond to actions, you may combine several reducers
* into a single reducer function by using `combineReducers`.
*
* @param {Function} reducer A function that returns the next state tree, given
* the current state tree and the action to handle.
*
* @param {any} [initialState] The initial state. You may optionally specify it
* to hydrate the state from the server in universal apps, or to restore a
* previously serialized user session.
* If you use `combineReducers` to produce the root reducer function, this must be
* an object with the same shape as `combineReducers` keys.
*
* @returns {Store} A Redux store that lets you read the state, dispatch actions
* and subscribe to changes.
*/
export default function createStore(reducer, initialState) {
if (typeof reducer !== 'function') {
throw new Error('Expected the reducer to be a function.');
} var currentReducer = reducer;
var currentState = initialState;
var listeners = [];
var isDispatching = false; /**
* Reads the state tree managed by the store.
*
* @returns {any} The current state tree of your application.
*/
// 这个方法没什么好讲的,返回当前的state
function getState() {
return currentState;
} /**
* Adds a change listener. It will be called any time an action is dispatched,
* and some part of the state tree may potentially have changed. You may then
* call `getState()` to read the current state tree inside the callback.
*
* @param {Function} listener A callback to be invoked on every dispatch.
* @returns {Function} A function to remove this change listener.
*/
// 很常见的监听函数添加方式,当store.dispatch 的时候被调用
// store.subscribe(listener) 返回一个方法(unscribe),可以用来取消监听
function subscribe(listener) {
listeners.push(listener);
var isSubscribed = true; return function unsubscribe() {
if (!isSubscribed) {
return;
} isSubscribed = false;
var index = listeners.indexOf(listener);
listeners.splice(index, 1);
};
} /**
* Dispatches an action. It is the only way to trigger a state change.
*
* The `reducer` function, used to create the store, will be called with the
* current state tree and the given `action`. Its return value will
* be considered the **next** state of the tree, and the change listeners
* will be notified.
*
* The base implementation only supports plain object actions. If you want to
* dispatch a Promise, an Observable, a thunk, or something else, you need to
* wrap your store creating function into the corresponding middleware. For
* example, see the documentation for the `redux-thunk` package. Even the
* middleware will eventually dispatch plain object actions using this method.
*
* @param {Object} action A plain object representing “what changed”. It is
* a good idea to keep actions serializable so you can record and replay user
* sessions, or use the time travelling `redux-devtools`. An action must have
* a `type` property which may not be `undefined`. It is a good idea to use
* string constants for action types.
*
* @returns {Object} For convenience, the same action object you dispatched.
*
* Note that, if you use a custom middleware, it may wrap `dispatch()` to
* return something else (for example, a Promise you can await).
*/
// 以下情况会报错
// 1. 传入的action不是一个对象
// 2. 传入的action是个对象,但是action.type 是undefined
function dispatch(action) {
if (!isPlainObject(action)) {
throw new Error(
'Actions must be plain objects. ' +
'Use custom middleware for async actions.'
);
} if (typeof action.type === 'undefined') {
throw new Error(
'Actions may not have an undefined "type" property. ' +
'Have you misspelled a constant?'
);
} if (isDispatching) {
throw new Error('Reducers may not dispatch actions.');
} try {
isDispatching = true;
// 就是这一句啦, 将 currentState 设置为 reducer(currentState, action) 返回的值
currentState = currentReducer(currentState, action);
} finally {
isDispatching = false;
} // 如果有监听函数,就顺序调用
listeners.slice().forEach(listener => listener()); // 最后,返回传入的action
return action;
} /**
* Replaces the reducer currently used by the store to calculate the state.
*
* You might need this if your app implements code splitting and you want to
* load some of the reducers dynamically. You might also need this if you
* implement a hot reloading mechanism for Redux.
*
* @param {Function} nextReducer The reducer for the store to use instead.
* @returns {void}
*/
function replaceReducer(nextReducer) {
currentReducer = nextReducer;
dispatch({ type: ActionTypes.INIT });
} // When a store is created, an "INIT" action is dispatched so that every
// reducer returns their initial state. This effectively populates
// the initial state tree.
//
// redux.createStore(reducer, initialState) 的时候, 内部会 自己调用 dispatch({ type: ActionTypes.INIT });
// 来完成state的初始化
dispatch({ type: ActionTypes.INIT }); // 返回的就是这个东东了,只有四个方法
return {
dispatch,
subscribe,
getState,
replaceReducer
};
}

源码解析:combineReducers.js

redux.combineReducers(reducerMap) 的作用在于合并多个reducer函数,并返回一个新的reducer函数。因此可以看到,combineReducers 返回了一个函数,并且该函数的参数同样是state、reducer。

可以先看伪代码感受下,最终 store.getState() 返回的state,大概会是这么个样子{todos: xx, filter: xx}。简单的说,state被拆分成了两份,TodoReducer的返回值赋值给了state.todos,FilterReducer的返回值赋值给了state.filter

function TodoReducer(state, action) {}
function FilterReducer(state, action) {} var finalReducers = redux.combineReducers({
todos: TodoReducer,
filter: FilterReducer
});

同样是直接上注解后的代码,记住几个关键就差不多了:

  1. combineReducers(reducerMap) 传入一个对象,并返回一个全新的reducer。调用方式跟跟普通的reducer一样,也是传入state、action。

  2. 通过combineReducers,对 store 的状态state进行拆分,

  3. reducerMap的key,就是 state 的key,而 调用对应的reducer返回的值,则是这个key对应的值。如上面的例子,state.todos == TodoReducer(state, action)

  4. redux.createStore(finalReducers, initialState) 调用时,同样会对 state 进行初始化。这个初始化跟通过普通的reducer进行初始化没多大区别。举例来说,如果 initialState.todos = undefined,那么 TodoReducer(state, action) 初始传入的state就是undefined;如果initialState.todos = [],那么 TodoReducer(state, action) 初始传入的state就是[];

  5. store.dispatch(action),finalReducers 里面,会遍历整个reducerMap,依次调用每个reducer,并将每个reducer返回的子state赋给state对应的key。

import { ActionTypes } from '../createStore';
import isPlainObject from '../utils/isPlainObject';
import mapValues from '../utils/mapValues';
import pick from '../utils/pick'; /* eslint-disable no-console */ function getUndefinedStateErrorMessage(key, action) {
var actionType = action && action.type;
var actionName = actionType && `"${actionType.toString()}"` || 'an action'; return (
`Reducer "${key}" returned undefined handling ${actionName}. ` +
`To ignore an action, you must explicitly return the previous state.`
);
} function getUnexpectedStateKeyWarningMessage(inputState, outputState, action) {
var reducerKeys = Object.keys(outputState);
var argumentName = action && action.type === ActionTypes.INIT ?
'initialState argument passed to createStore' :
'previous state received by the reducer'; if (reducerKeys.length === 0) {
return (
'Store does not have a valid reducer. Make sure the argument passed ' +
'to combineReducers is an object whose values are reducers.'
);
} if (!isPlainObject(inputState)) {
return (
`The ${argumentName} has unexpected type of "` +
({}).toString.call(inputState).match(/\s([a-z|A-Z]+)/)[1] +
`". Expected argument to be an object with the following ` +
`keys: "${reducerKeys.join('", "')}"`
);
} var unexpectedKeys = Object.keys(inputState).filter(
key => reducerKeys.indexOf(key) < 0
); if (unexpectedKeys.length > 0) {
return (
`Unexpected ${unexpectedKeys.length > 1 ? 'keys' : 'key'} ` +
`"${unexpectedKeys.join('", "')}" found in ${argumentName}. ` +
`Expected to find one of the known reducer keys instead: ` +
`"${reducerKeys.join('", "')}". Unexpected keys will be ignored.`
);
}
} // 对reducer做合法性检测
// store = Redux.createStore(reducer, initialState) -->
// currentState = initialState
// currentState = currentReducer(currentState, action);
//
// 从调用关系,调用时机来看, store.getState() 的初始值(currentState)
// 为 currentReducer(initialState, { type: ActionTypes.INIT })
//
// 1. 在初始化阶段,reducer 传入的 state 值是 undefined,此时,需要返回初始state,且初始state不能为undefined
// 2. 当传入不认识的 actionType 时, reducer(state, {type}) 返回的不能是undefined
// 3. redux/ 这个 namespace 下的action 不应该做处理,直接返回 currentState 就行 (谁运气这么差会去用这种actionType...)
function assertReducerSanity(reducers) {
Object.keys(reducers).forEach(key => {
var reducer = reducers[key];
var initialState = reducer(undefined, { type: ActionTypes.INIT }); if (typeof initialState === 'undefined') {
throw new Error(
`Reducer "${key}" returned undefined during initialization. ` +
`If the state passed to the reducer is undefined, you must ` +
`explicitly return the initial state. The initial state may ` +
`not be undefined.`
);
} var type = '@@redux/PROBE_UNKNOWN_ACTION_' + Math.random().toString(36).substring(7).split('').join('.');
if (typeof reducer(undefined, { type }) === 'undefined') {
throw new Error(
`Reducer "${key}" returned undefined when probed with a random type. ` +
`Don't try to handle ${ActionTypes.INIT} or other actions in "redux/*" ` +
`namespace. They are considered private. Instead, you must return the ` +
`current state for any unknown actions, unless it is undefined, ` +
`in which case you must return the initial state, regardless of the ` +
`action type. The initial state may not be undefined.`
);
}
});
} /**
* Turns an object whose values are different reducer functions, into a single
* reducer function. It will call every child reducer, and gather their results
* into a single state object, whose keys correspond to the keys of the passed
* reducer functions.
*
* @param {Object} reducers An object whose values correspond to different
* reducer functions that need to be combined into one. One handy way to obtain
* it is to use ES6 `import * as reducers` syntax. The reducers may never return
* undefined for any action. Instead, they should return their initial state
* if the state passed to them was undefined, and the current state for any
* unrecognized action.
*
* @returns {Function} A reducer function that invokes every reducer inside the
* passed object, and builds a state object with the same shape.
*/ export default function combineReducers(reducers) {
// 返回一个对象, key => value 且value是function(其实就是过滤掉非function)
var finalReducers = pick(reducers, (val) => typeof val === 'function');
var sanityError; try {
// 对所有的子reducer 做一些合法性断言,如果没有出错再继续下面的处理
// 合法性断言的内容,见API注释
assertReducerSanity(finalReducers);
} catch (e) {
sanityError = e;
} // 所有的 key: value,将value置成了undefined,费解...
// 总而言之, 初始state 就是 类似 {hello: undefined, world: undefined} 的东东
// TODO 确认这里的逻辑
var defaultState = mapValues(finalReducers, () => undefined); return function combination(state = defaultState, action) {
if (sanityError) {
throw sanityError;
} var hasChanged = false;
// 这段代码,简单的说,就是循环一遍 finalState[key] = fn(reducer, key)
var finalState = mapValues(finalReducers, (reducer, key) => {
var previousStateForKey = state[key];
var nextStateForKey = reducer(previousStateForKey, action);
if (typeof nextStateForKey === 'undefined') {
// 其他一个reducer返回的是undefined,于是挂啦...抛出错误
var errorMessage = getUndefinedStateErrorMessage(key, action);
throw new Error(errorMessage);
}
// 这段代码有些费解,从redux的设计理念上来讲,除了不认识的action type,其他情况都应该返回全新的state
// 也就是说
// 1. action type 认识,返回新的state,于是这里 hasChanged 为 true
// 2. action type 不认识,返回原来的state,于是这里 hasChanged 为 false
// 3. 不管action type 是否认识, 在原来的state上修改,但是返回的是修改后的state(没有返回拷贝),那么,hasChanged还是为false
hasChanged = hasChanged || nextStateForKey !== previousStateForKey;
return nextStateForKey;
}); // 开发环境中(于是记得在生产环境去掉)
// 后面再研究这段代码,毕竟不是主线路...
if (process.env.NODE_ENV !== 'production') {
var warningMessage = getUnexpectedStateKeyWarningMessage(state, finalState, action);
if (warningMessage) {
console.error(warningMessage);
}
} return hasChanged ? finalState : state;
};
}

源码解析:bindActionCreator.js

别看API注释一大堆,除去合法性检查,关键代码其实就只有几句。先看个简单例子可能方便理解一些。看完之后可能会觉得,不就是对store.dispatch 的调用进行了便捷处理嘛。。。

var addTodo = function(text){
return {
type: 'add_todo',
text: text
};
}; var addTodos = function(){
return {
type: 'add_todos',
items: Array.prototype.slice.call(arguments, 0)
};
}; var reducer = function(state, action){
switch (action.type) {
case 'add_todo':
return state.concat(action.text);
case 'add_todos':
return state.concat(action.items);
default:
return state;
}
}; var store = redux.createStore(reducer, []);
// 注意,关键代码在这里
var actions = redux.bindActionCreators({
addTodo: addTodo,
addTodos: addTodos
}, store.dispatch); console.log('state is: ' + store.getState()); store.dispatch({type: 'add_todo', text: '读书'});
store.dispatch({type: 'add_todos', items: ['阅读', '睡觉']});
console.log('state is: ' + store.getState()); // state is: 读书,阅读,睡觉 actions.addTodo('看电影');
console.log('state is: ' + store.getState()); // state is: 读书,阅读,睡觉,看电影 actions.addTodos(['刷牙', '洗澡']);
console.log('state is: ' + store.getState()); // state is: 读书,阅读,睡觉,看电影,刷牙,洗澡

所以,直接看代码吧,挺简单的。

import mapValues from '../utils/mapValues';

function bindActionCreator(actionCreator, dispatch) {
return (...args) => dispatch(actionCreator(...args));
} /**
* Turns an object whose values are action creators, into an object with the
* same keys, but with every function wrapped into a `dispatch` call so they
* may be invoked directly. This is just a convenience method, as you can call
* `store.dispatch(MyActionCreators.doSomething())` yourself just fine.
*
* For convenience, you can also pass a single function as the first argument,
* and get a function in return.
*
* @param {Function|Object} actionCreators An object whose values are action
* creator functions. One handy way to obtain it is to use ES6 `import * as`
* syntax. You may also pass a single function.
*
* @param {Function} dispatch The `dispatch` function available on your Redux
* store.
*
* @returns {Function|Object} The object mimicking the original object, but with
* every action creator wrapped into the `dispatch` call. If you passed a
* function as `actionCreators`, the return value will also be a single
* function.
*/
// 假设 actionCreators === {addTodo: addTodo, removeTodo: removeTodo}
// 简单的来说 bindActionCreators(actionCreators, dispatch)
// 最后返回的是:
// {
// addTodo: function(text){
// dispatch( actionCreators.addTodo(text) );
// },
// removeTodo: function(text){
// dispatch( actionCreators.removeTodo(text) );
// }
// }
//
// 或者说 actionCreators === addTodo (addTodo 为 actionCreator)
// 最后返回的是
// function() {
// dispatch(actionCreators());
// }
export default function bindActionCreators(actionCreators, dispatch) {
if (typeof actionCreators === 'function') {
return bindActionCreator(actionCreators, dispatch);
} if (typeof actionCreators !== 'object' || actionCreators === null || actionCreators === undefined) { // eslint-disable-line no-eq-null
throw new Error(
`bindActionCreators expected an object or a function, instead received ${actionCreators === null ? 'null' : typeof actionCreators}. ` +
`Did you write "import ActionCreators from" instead of "import * as ActionCreators from"?`
);
} return mapValues(actionCreators, actionCreator =>
bindActionCreator(actionCreator, dispatch)
);
}

源码解析:applyMiddleware.js

中间件应该是redux源码里面最绕的一部分,虽然看懂后,有一种“啊~原来不过如此”的感觉,但一开始还真是看的晕头转向的,API的说明、中间件的编写、applyMiddleware的源码实现,都不是那么好理解。

在继续源码解析之前,推荐看下官方文档对于middleware的说明,链接传送门:http://camsong.github.io/redux-in-chinese/docs/advanced/Middleware.html

虽然文档死长死长,但硬着头皮看完,还是有所收获的,终于知道 applyMiddleware 的实现这么绕了。。。

例子:redux-thunk

用redux处理过异步请求的同学应该用过redux-thunk,我们来看下他的源码,奇短无比,别说你的小伙伴了,我的小伙伴都惊呆了。

export default function thunkMiddleware({ dispatch, getState }) {
return next => action =>
typeof action === 'function' ?
action(dispatch, getState) :
next(action);
}

翻译成ES5,是这样子滴,之后你再看其他中间件的实现,其实都大同小异,下面我们写个自定义中间件,基本就可以看出点门路来。

'use strict';

Object.defineProperty(exports, "__esModule", {
value: true
});
exports.default = thunkMiddleware;
function thunkMiddleware(store) {
var dispatch = store.dispatch;
var getState = store.getState; return function (next) {
return function (action) {
return typeof action === 'function' ? action(dispatch, getState) : next(action);
};
};
}
module.exports = exports['default'];

自定义中间件:logger

先看logger的实现

    function middleware(store){
return function(next){
return function(action){
return next(action);
}
}
}

基本看出中间件声明的模版来了,就是下面这个样子。下面结合applyMiddleware的调用,来说明store、next、action 几个参数。

    function logger(store){
return function(next){
return function(action){
console.log('logger: dispatching ' + action.type);
var result = next(action);
console.log('logger: next state ' + result);
return result;
}
}
}

applyMiddleware调用例子

完整的示例代码见本小节最后面。可以看到:

  1. applyMiddleware 的调用方式为 applyMiddleware(...middlewares)(react.createStore)。其实这里直接先创建 store,然后applyMiddleware(...middlewares)(store) 也很容易实现相同的效果,不过作者是故意这样设计的,为了避免在同一个store上多次应用同一个middlerware(参考官方文档:尝试 #6: “单纯”地使用 Middleware )

  2. 中间件顶层的store参数,并不是常规的store,虽然它也有 getState、dispatch 两个方法

        // 上面的store参数,其实就是这个对象
    // 其中,store 为内部的store,我们在外面 storeWithMiddleWare.dipatch的时候,内部实现是转成 store.dispatch
    // 此外,可以看到 middlewareAPI.dispatch 方法,是最终封装后的dispatch(千万注意,如果在中间件内部 调用 store.dispatch,可能导致死循环 )
    var middlewareAPI = {
    getState: store.getState,
    // 最后面, dispatch 被覆盖, 变成包装后的 dispatch 方法
    dispatch: (action) => dispatch(action)
    };
  3. 第二层的next函数,其实是一个“dispatch”方法。熟悉express的同学大概可以猜到它的作用。storeWithMiddleWare.dispatch(action) 的时候,会顺序进入各个中间件(按照定义时的顺序)。从当前的例子来看,大约如下,其实就是柯里化啦~:

storeWithMiddleWare.dispatch(action) --> logger(store)(next)(action) --> timer(store)(next)(action) --> store.dispatch(action)

完整的示例代码

    function reducer(state, action){
if(typeof state==='undefined') state = []; switch(action.type){
case 'add_todo':
return state.concat(action.text);
default:
return state;
}
} function addTodo(text){
return {
type: 'add_todo',
text: text
};
} // 这里的 store,并不是 redux.createStore(reducer, initialState) 出来的 store
// 而是 {getState: store.getState, dispatch: function() { store.dispatch(action); }}
//
function logger(store){
//
return function(next){
return function(action){
console.log('logger: dispatching ' + action.type);
var result = next(action);
console.log('logger: next state ' + result);
return result;
}
}
} function timer(store){
return function(next){
return function(action){
console.log('timer: dispatching ' + action.type);
var result = next(action);
console.log('timer: next state ' + result);
return result;
}
}
} var createStoreWidthMiddleware = redux.applyMiddleware(
logger,
timer
)(redux.createStore); var storeWithMiddleWare = createStoreWidthMiddleware(reducer);
storeWithMiddleWare.subscribe(function(){
console.log('subscribe: state is : ' + storeWithMiddleWare.getState());
});
console.log( storeWithMiddleWare.dispatch(addTodo('reading')) );

源码解析

再次说下,建议先看下官方文档对中间件的介绍,不然可能会有点晕。

import compose from './compose';

/**
* Creates a store enhancer that applies middleware to the dispatch method
* of the Redux store. This is handy for a variety of tasks, such as expressing
* asynchronous actions in a concise manner, or logging every action payload.
*
* See `redux-thunk` package as an example of the Redux middleware.
*
* Because middleware is potentially asynchronous, this should be the first
* store enhancer in the composition chain.
*
* Note that each middleware will be given the `dispatch` and `getState` functions
* as named arguments.
*
* @param {...Function} middlewares The middleware chain to be applied.
* @returns {Function} A store enhancer applying the middleware.
*/
/*
从调用方法 applyMiddleware(...middlewares)(Redux.createStore) 可以看出
next 参数实际上是 Redux.createStore. 而 Redux.createStore 的调用方式为 Redux.createStore(reducer, initialState)
所以 applyMiddleware(...middlewares)
1. 参数: Redux.createStore
2. 返回值:一个function, 跟 Redux.createStore 接受的参数一样 */
export default function applyMiddleware(...middlewares) {
return (next) => (reducer, initialState) => {
// 内部先创建一个store (相当于直接调用 Redux.createStore(reducer, initialState))
var store = next(reducer, initialState);
// 保存最初始的store.dispatch
var dispatch = store.dispatch;
var chain = []; var middlewareAPI = {
getState: store.getState,
// 最后面, dispatch 被覆盖, 变成包装后的 dispatch 方法
dispatch: (action) => dispatch(action)
};
// 返回一个数组
// 贴个例子在这里做参考,redux-thunk
// function thunkMiddleware(store) {
// var dispatch = store.dispatch;
// var getState = store.getState;
//
// 这里的next其实就是dispatch
// return function (next) {
// return function (action) {
// return typeof action === 'function' ? action(dispatch, getState) : next(action);
// };
// };
//}
/*
chain 是个数组, 参考上面的 middlleware (redux-thunk),可以看到,chain的每个元素为如下形式的function
并且, 传入的 store.getState 为原始的 store.getState,而 dispatch则是包装后的 dispatch(不是原始的store.dispatch)
似乎是为了确保, 在每个middleware里调用 dispatch(action), 最终都是 用原始的 store.dispatch(action)
避免 store.dispatch 被覆盖, 导致middleware 顺序调用的过程中, store.dispatch的值变化 --> store.dispatch 返回的值可能会有不同
违背 redux 的设计理念 这里的 next 则为 原始的 store.dispatch (见下面 compose(...chain)(store.dispatch) )
function (next) {
return function (action) { }
}
*/
chain = middlewares.map(middleware => middleware(middlewareAPI)); // compose(...chain)(store.dispatch) 返回了一个function
// 伪代码如下,
// function (action) {
// middleware(store)(store.dispatch);
// }
dispatch = compose(...chain)(store.dispatch); // 从右到左, middleware1( middleware2( middleware3(dispatch) ) ) // 于是,最终调用 applyMiddleware(...middlewares)(Redux.createStore)
// 返回的 store, getState,subscribe 方法都是原始的那个 store.getState, store.subscribe
// 至于dispatch是封装过的
return {
...store,
dispatch
};
};
}

相关链接

官方文档:http://camsong.github.io/redux-in-chinese/docs/advanced/Middleware.html
源码解析github地址:https://github.com/chyingp/redux-source-insight
源码解析相关代码示例:https://github.com/chyingp/redux-source-insight/tree/master/examples

Redux系列x:源码解析的更多相关文章

  1. Netty系列之源码解析(一)

    本文首发于微信公众号[猿灯塔],转载引用请说明出处 接下来的时间灯塔君持续更新Netty系列一共九篇 当前:Netty 源码解析(一)开始 Netty 源码解析(二): Netty 的 Channel ...

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

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

  3. JAVA常用集合源码解析系列-ArrayList源码解析(基于JDK8)

    文章系作者原创,如有转载请注明出处,如有雷同,那就雷同吧~(who care!) 一.写在前面 这是源码分析计划的第一篇,博主准备把一些常用的集合源码过一遍,比如:ArrayList.HashMap及 ...

  4. Android源码解析系列

    转载请标明出处:一片枫叶的专栏 知乎上看了一篇非常不错的博文:有没有必要阅读Android源码 看完之后痛定思过,平时所学往往是知其然然不知其所以然,所以为了更好的深入Android体系,决定学习an ...

  5. quartz源码解析(一)

    本文的起因源于一次quartz的异常,在win2003正常运行的程序放在linux环境就抛出异常了,虽然找出异常没花我多长时间,不过由此加深了对quzrtz的了解:古人说,三折肱,为良医,说明经验对于 ...

  6. Sentinel源码解析二(Slot总览)

    写在前面 本文继续来分析Sentinel的源码,上篇文章对Sentinel的调用过程做了深入分析,主要涉及到了两个概念:插槽链和Node节点.那么接下来我们就根据插槽链的调用关系来依次分析每个插槽(s ...

  7. Sentinel源码解析一(流程总览)

    引言 Sentinel作为ali开源的一款轻量级流控框架,主要以流量为切入点,从流量控制.熔断降级.系统负载保护等多个维度来帮助用户保护服务的稳定性.相比于Hystrix,Sentinel的设计更加简 ...

  8. 【原】Android热更新开源项目Tinker源码解析系列之三:so热更新

    本系列将从以下三个方面对Tinker进行源码解析: Android热更新开源项目Tinker源码解析系列之一:Dex热更新 Android热更新开源项目Tinker源码解析系列之二:资源文件热更新 A ...

  9. 【原】Android热更新开源项目Tinker源码解析系列之一:Dex热更新

    [原]Android热更新开源项目Tinker源码解析系列之一:Dex热更新 Tinker是微信的第一个开源项目,主要用于安卓应用bug的热修复和功能的迭代. Tinker github地址:http ...

随机推荐

  1. VS2010/2013 运行是很卡的加速方案

    前段时间为了一个项目而把VS2008换成了VS2010,结果原本就不堪重负的本本跑起VS2010来那更是慢得没话说,于是看了遍VS2010选项,又从网上到处找资料找优化方法,总算使我的VS2010跑得 ...

  2. 10分钟让你明白MySQL是如何利用索引的

    一.前言 在MySQL中进行SQL优化的时候,经常会在一些情况下,对MySQL能否利用索引有一些迷惑. 譬如: MySQL 在遇到范围查询条件的时候就停止匹配了,那么到底是哪些范围条件? MySQL ...

  3. Cisco ASA 使用ASDM 配置管理口 方法

    CISCO ASA防火墙ASDM安装和配置 准备一条串口线一边接台式机或笔记本一边接防火墙的CONSOLE 接口,通过CRT或者超级终端连接ASA在用ASDM图形管理界面之前须在串口下输入一些命令开启 ...

  4. Security Software Engineer

    Security Software Engineer Are you excited to be part of the VR revolution and work on cutting edge ...

  5. Linux nmap命令详解

    nmap,也就是Network Mapper,是Linux下的网络扫描和嗅探工具包. nmap是在网络安全渗透测试中经常会用到的强大的扫描器.功能之强大,不言而喻.下面介绍一下它的几种扫描命令.具体的 ...

  6. 网络基础之IP地址和子网掩码

    IP地址 IP是英文Internet Protocol的缩写,意思是"网络之间互连的协议",也就是为计算机网络相互连接进行通信而设计的协议.在因特网中,它是能使连接到网上的所有计算 ...

  7. C++设计模式 ==> 策略模式与简单工厂模式结合

    简介 策略模式相较之于简单工厂模式适用于生产方法经常变化且方法较为繁多的情况,因为生产方法时常变化就会需要频繁修改工厂类,违背了开闭原则,这时就可以用策略选择类由客户端根据需求动态切换策略.且策略模式 ...

  8. 漏洞扫描--openvas

    操作实例演示 0.登录openvas 点击“openvas start”启动openvas相关服务,服务启动成功之后!在浏览器输入网址:https://127.0.0.1/login/login.ht ...

  9. 【Alpha 冲刺】 11/12

    今日任务总结 人员 今日原定任务 完成情况 遇到问题 贡献值 胡武成 完成app端api编写 未完成 文件上传api还没完成 孙浩楷 1. 与后端交接, 2. 完成图片在线编辑插件引入 未完成 陷入僵 ...

  10. Docker 安装 - Docker 与前端(一)

    Docker 是一个开源的容器引擎,可以方便的对容器进行管理.作为一种新兴的虚拟化方式,跟传统的虚拟化方式相比具有众多优势.<Docker 遇见前端>系列文章,旨在记录如何通过 docke ...