微信公众号搜"智元新知"关注
微信扫一扫可直接关注哦!

Redux系列x:源码分析

写在前面

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的说明,链接传送门:

虽然文档死长死长,但硬着头皮看完,还是有所收获的,终于知道 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(参考

  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)
    };
  1. 第二层的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')) );</code></pre>

源码解析

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

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 r<a href="https://www.jb51.cc/tag/edux/" target="_blank" class="keywords">edux</a>-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 <a href="https://www.jb51.cc/tag/dis/" target="_blank" class="keywords">dis</a>patch 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,变成包装后的 <a href="https://www.jb51.cc/tag/dis/" target="_blank" class="keywords">dis</a>patch <a href="https://www.jb51.cc/tag/fangfa/" target="_blank" class="keywords">方法</a>
  <a href="https://www.jb51.cc/tag/dis/" target="_blank" class="keywords">dis</a>patch: (action) => <a href="https://www.jb51.cc/tag/dis/" target="_blank" class="keywords">dis</a>patch(action)
};
// 返回<a href="https://www.jb51.cc/tag/yige/" target="_blank" class="keywords">一个</a>数组
// 贴个例子<a href="https://www.jb51.cc/tag/zaizheli/" target="_blank" class="keywords">在这里</a>做参考,r<a href="https://www.jb51.cc/tag/edux/" target="_blank" class="keywords">edux</a>-thunk
// function thunkMiddleware(store) {
//  var <a href="https://www.jb51.cc/tag/dis/" target="_blank" class="keywords">dis</a>patch = store.<a href="https://www.jb51.cc/tag/dis/" target="_blank" class="keywords">dis</a>patch;
//  var getState = store.getState;
//
//  这里的next其实就是<a href="https://www.jb51.cc/tag/dis/" target="_blank" class="keywords">dis</a>patch
//  return function (next) {
//    return function (action) {
//      return typeof action === 'function' ? action(<a href="https://www.jb51.cc/tag/dis/" target="_blank" class="keywords">dis</a>patch,getState) : next(action);
//    };
//  };
//}
/*
  chain 是个数组,参考上面的 middlleware (r<a href="https://www.jb51.cc/tag/edux/" target="_blank" class="keywords">edux</a>-thunk),可以看到,chain的每个元素为如下形式的function
  并且,传入的 store.getState 为原始的 store.getState,而 <a href="https://www.jb51.cc/tag/dis/" target="_blank" class="keywords">dis</a>patch则是包装后的 <a href="https://www.jb51.cc/tag/dis/" target="_blank" class="keywords">dis</a>patch(不是原始的store.<a href="https://www.jb51.cc/tag/dis/" target="_blank" class="keywords">dis</a>patch)
  似乎是为了确保,在每个middleware里<a href="https://www.jb51.cc/tag/diaoyong/" target="_blank" class="keywords">调用</a> <a href="https://www.jb51.cc/tag/dis/" target="_blank" class="keywords">dis</a>patch(action),最终都是 用原始的 store.<a href="https://www.jb51.cc/tag/dis/" target="_blank" class="keywords">dis</a>patch(action)
  避免 store.<a href="https://www.jb51.cc/tag/dis/" target="_blank" class="keywords">dis</a>patch 被覆盖,导致middleware 顺序<a href="https://www.jb51.cc/tag/diaoyong/" target="_blank" class="keywords">调用</a>的过程中,store.<a href="https://www.jb51.cc/tag/dis/" target="_blank" class="keywords">dis</a>patch的值变化 --> store.<a href="https://www.jb51.cc/tag/dis/" target="_blank" class="keywords">dis</a>patch 返回的值可能会有不同
  违背 r<a href="https://www.jb51.cc/tag/edux/" target="_blank" class="keywords">edux</a> 的设计理念

  这里的 next 则为 原始的 store.<a href="https://www.jb51.cc/tag/dis/" target="_blank" class="keywords">dis</a>patch (见下面 compose(...chain)(store.<a href="https://www.jb51.cc/tag/dis/" target="_blank" class="keywords">dis</a>patch) )
  function (next) {
    return function (action) {

    }
  }
 */
chain = middlewares.map(middleware => middleware(middlewareAPI));

// compose(...chain)(store.<a href="https://www.jb51.cc/tag/dis/" target="_blank" class="keywords">dis</a>patch) 返回了<a href="https://www.jb51.cc/tag/yige/" target="_blank" class="keywords">一个</a>function
// 伪<a href="https://www.jb51.cc/tag/daima/" target="_blank" class="keywords">代码</a>如下,// function (action) {
//   middleware(store)(store.<a href="https://www.jb51.cc/tag/dis/" target="_blank" class="keywords">dis</a>patch);
// }
<a href="https://www.jb51.cc/tag/dis/" target="_blank" class="keywords">dis</a>patch = compose(...chain)(store.<a href="https://www.jb51.cc/tag/dis/" target="_blank" class="keywords">dis</a>patch);  // 从右到左,middleware1( middleware2( middleware3(<a href="https://www.jb51.cc/tag/dis/" target="_blank" class="keywords">dis</a>patch) ) )

// 于是,最终<a href="https://www.jb51.cc/tag/diaoyong/" target="_blank" class="keywords">调用</a> applyMiddleware(...middlewares)(R<a href="https://www.jb51.cc/tag/edux/" target="_blank" class="keywords">edux</a>.createStore)
// 返回的 store,subscribe <a href="https://www.jb51.cc/tag/fangfa/" target="_blank" class="keywords">方法</a>都是原始的那个 store.getState,store.subscribe
// 至于<a href="https://www.jb51.cc/tag/dis/" target="_blank" class="keywords">dis</a>patch是封装过的
return {
  ...store,<a href="https://www.jb51.cc/tag/dis/" target="_blank" class="keywords">dis</a>patch
};

};
}

相关链接

官方文档:
源码解析github地址:

源码解析相关
代码示例:edux-source-insight/tree/master/examples">https://github.com/chyingp/redux-source-insight/tree/master/examples

版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。

相关推荐