https://github.com/reduxjs/redux 版本 4.0.0html
先了解一下redux是怎麼用的,此處摘抄自阮一峯老師的《Redux 入門教程》git
// Web 應用是一個狀態機,視圖與狀態是一一對應的 // 全部的狀態,保存在一個對象裏面 // store 是保存數據的地方 // 建立 store import { createStore } from 'redux' const store = createStore(fn) // state 是某一時刻 store 的快照,一個 state 對應一個 view // 可經過 getState() 獲取 const state = store.getState() // Action 是一個對象 用來表示 view 發出的改變 state 的通知 // type 是必須的 其餘屬性能夠自由設置 const action = { type: 'ADD_TODO', payload: 'Learn Redux' } // 同一種類型的 action 能夠寫一個函數生成 const ADD_TODO = '添加 TODO' // 生成 action 的函數: Action Creator function addTodo(text) { return { type: ADD_TODO, text } } const action = addTodo('Learn Redux') // store.dispatch()是 View 發出 Action 的惟一方法。 store.dispatch(action) // reducer 是 store 接收 state 返回新的 state 的過程 const defaultState = 0 // reducer 接收 action 返回新的 state const reducer = (state = defaultState, action) => { switch(action.type) { case: 'ADD': return state + action.payload default: return state } } const state = reducer(1, { type: 'ADD', payload: 2 }) // 建立 store 時傳入 reducer 當調用 store.dispatch 時將自動調用 reducer const store = createStore(reducer) /* reducer 是一個純函數,純函數要求: - 不得改寫參數 - 不能調用系統 I/O 的API - 不能調用Date.now()或者Math.random()等不純的方法,由於每次會獲得不同的結果 */ // store.subscribe方法設置監聽函數,一旦 State 發生變化,就自動執行這個函數 // 返回解除監聽函數 let unsubscribe = store.subsribe(() => { console.log(store.getState) }) unsubscribe() // 解除監聽 /* store 提供的三個方法 - store.getState() - store.dispatch() - store.subscribe() */ // createStore方法還能夠接受第二個參數,表示 State 的最初狀態。這一般是服務器給出的。 // !這個初始值會覆蓋 Reducer 函數默認的初始值 let store = createStore(todoApp, STATE_FROM_SERVER) // createStore 的簡單實現 const createStore = (reducer) => { let state let listeners = [] const getState = () => state const dispatch = action => { state = reducer(state, action) listeners.forEach(listener => listener()) } const subscribe = listener => { listeners.push(listener) return () => { listeners = listeners.filter(l => l !== listener) } } dispatch({}) return { getState, dispatch, subscribe } } // 能夠經過 combineReducers 來將多個 Reducer 合爲一個 import { combineReducers } from 'redux' const chatReducer = combineReducers({ chatLog, statusMessage, userName }) // combineReducer 的簡單實現 const combineReducers = reducers => { return (state = {}, action) => Object.keys(reducers).reduce( (nextState, key) => { nextState[key] = reducers[key](state[key], action) return nextState }, {} ) }
工做流程github
Redux Flow dispatch(action) (previousState, action) Action Creators ======> Store ======> Reducers ^ || <====== \_ || (newState) \_ (state) || \_ || (view opt)\_ \/ \--- React Comonents
OK 能夠開始看源碼了~ 網上Redux源碼分析的博客真的很是多.. 不過當你知道他的源碼究竟有多短 就能理解了hhh web
combineReducers.js express
代碼一共179行 可能是錯誤處理 我先將錯誤處理所有刪掉 便只剩28行.....編程
思路就是建立一個對象 將 Reducer 所有放進去redux
當Action傳進來的時候 就讓每個Reducer去處理這個actionapi
每一個Reducer都有一個對應的key 只處理state中對應字段 state[key] 沒有Reducer對應的字段會被忽略服務器
截取出核心代碼 + 用法、感受並不須要註釋、邏輯都很直接app
function combineReducers(reducers) { const reducerKeys = Object.keys(reducers) const finalReducers = {} for (let i = 0; i < reducerKeys.length; i++) { const key = reducerKeys[i] if (typeof reducers[key] === 'function') { finalReducers[key] = reducers[key] } } const finalReducerKeys = Object.keys(finalReducers) return function combination(state = {}, action) { let hasChanged = false const nextState = {} for (let i = 0; i < finalReducerKeys.length; i++) { const key = finalReducerKeys[i] const reducer = finalReducers[key] const previousStateForKey = state[key] const nextStateForKey = reducer(previousStateForKey, action) nextState[key] = nextStateForKey hasChanged = hasChanged || nextStateForKey !== previousStateForKey } // 若是state每個key都沒有被修改 就直接返回原state return hasChanged ? nextState : state } } /***************** 下面是簡單的用法實例 *****************/ function todos(state = [], action) { switch (action.type) { case 'ADD_TODO': return state.concat(action.text) default: return state } } function counter(state = 0, action) { switch (action.type) { case 'INCREMENT': return state + 1 case 'DECREMENT': return state - 1 default: return state } } let reducer = combineReducers({ list: todos, number: counter }) let state = { list: [], number: 0, otherKey: 'no reducer match will be ignore' } console.log(state) // { list: [], number: 0, otherKey: 'no reducer match will be ignore' } state = reducer(state, { type: 'ADD_TODO', text: 'study' }) console.log(state) // { list: [ 'study' ], number: 0 } state = reducer(state, { type: 'ADD_TODO', text: 'sleep' }) console.log(state) // { list: [ 'study', 'sleep' ], number: 0 } state = reducer(state, { type: 'INCREMENT' }) console.log(state) // { list: [ 'study', 'sleep' ], number: 1 }
combineReducers.js 源碼
import ActionTypes from './utils/actionTypes' import warning from './utils/warning' import isPlainObject from './utils/isPlainObject' function getUndefinedStateErrorMessage(key, action) { const actionType = action && action.type const actionDescription = (actionType && `action "${String(actionType)}"`) || 'an action' return ( `Given ${actionDescription}, reducer "${key}" returned undefined. ` + `To ignore an action, you must explicitly return the previous state. ` + `If you want this reducer to hold no value, you can return null instead of undefined.` ) } function getUnexpectedStateShapeWarningMessage( inputState, reducers, action, unexpectedKeyCache ) { const reducerKeys = Object.keys(reducers) const argumentName = action && action.type === ActionTypes.INIT ? 'preloadedState 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)) { // 但願 inputState 是一個簡單對象:經過 new Object() 、 {} 建立 (Object.create(null) 這裏好像是不合法的 // [object Array] 中提取 'Array' // Object.prototype.toString.call(inputState).match(/\s([a-z|A-Z]+)/)[1] 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('", "')}"` ) } // 檢查全部Reducer都沒有處理到的key ( 此處實在不解 unexpectedKeyCache 到底何用= = const unexpectedKeys = Object.keys(inputState).filter( key => !reducers.hasOwnProperty(key) && !unexpectedKeyCache[key] ) unexpectedKeys.forEach(key => { unexpectedKeyCache[key] = true }) // 替換 store 的 Reducer 時會調用 dispatch({ type: ActionTypes.REPLACE }) if (action && action.type === ActionTypes.REPLACE) return 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.` ) } } function assertReducerShape(reducers) { Object.keys(reducers).forEach(key => { const reducer = reducers[key] const initialState = reducer(undefined, { type: ActionTypes.INIT }) // Reducer"$ {key}"在初始化時返回undefined。若是傳遞給reducer的狀態未定義,你必須明確返回初始狀態。 // 初始狀態能夠是不可定義。若是你不想爲這個reducer設置一個值,你可使用null而不是undefined。 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. If you don't want to set a value for this reducer, ` + `you can use null instead of undefined.` ) } if ( typeof reducer(undefined, { type: ActionTypes.PROBE_UNKNOWN_ACTION() }) === 'undefined' ) { // 當使用隨機類型探測Reducer${key}時返回undefined。 // 不要試圖處理${ActionTypes.INIT}或者其餘在"redux/*"命名空間的動做。它們被認爲是私有的。 // 相反,當你遇到任何未知動做時,你必須返回當前的state,除非當前state是undefined, // 那樣你要返回初始狀態,而無論動做類型。初始狀態不能夠是undefined,但能夠爲null 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, but can be null.` ) } }) } /** * 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) { const reducerKeys = Object.keys(reducers) const finalReducers = {} for (let i = 0; i < reducerKeys.length; i++) { const key = reducerKeys[i] if (process.env.NODE_ENV !== 'production') { if (typeof reducers[key] === 'undefined') { warning(`No reducer provided for key "${key}"`) } } if (typeof reducers[key] === 'function') { finalReducers[key] = reducers[key] } } const finalReducerKeys = Object.keys(finalReducers) let unexpectedKeyCache if (process.env.NODE_ENV !== 'production') { unexpectedKeyCache = {} } let shapeAssertionError try { // 判斷每一個reducer都有初始值和對於未知action返回原state assertReducerShape(finalReducers) } catch (e) { shapeAssertionError = e } return function combination(state = {}, action) { if (shapeAssertionError) { throw shapeAssertionError } if (process.env.NODE_ENV !== 'production') { const warningMessage = getUnexpectedStateShapeWarningMessage( state, finalReducers, action, unexpectedKeyCache ) if (warningMessage) { warning(warningMessage) } } let hasChanged = false const nextState = {} for (let i = 0; i < finalReducerKeys.length; i++) { const key = finalReducerKeys[i] const reducer = finalReducers[key] const previousStateForKey = state[key] const nextStateForKey = reducer(previousStateForKey, action) if (typeof nextStateForKey === 'undefined') { const errorMessage = getUndefinedStateErrorMessage(key, action) throw new Error(errorMessage) } nextState[key] = nextStateForKey hasChanged = hasChanged || nextStateForKey !== previousStateForKey } // 若是state每個key都沒有被修改 就直接返回原state return hasChanged ? nextState : state } }
utils/actionTypes.js
// 生成隨機字符串的方式能夠參考下 // 隨機數轉36進制 能夠生成 '0-9a-z' 的隨機字符串 const randomString = () => Math.random() .toString(36) .substring(7) .split('') .join('.') // 私有action類型 (其實就至關於未知的action 返回當前狀態就行了 // 若是當前 state 爲undefined 就返回 Reducer設置的初始 state const ActionTypes = { INIT: `@@redux/INIT${randomString()}`, REPLACE: `@@redux/REPLACE${randomString()}`, PROBE_UNKNOWN_ACTION: () => `@@redux/PROBE_UNKNOWN_ACTION${randomString()}` } export default ActionTypes
createStore.js
是redux核心代碼,不過這個沒有什麼難理解的地方
import $$observable from 'symbol-observable' import ActionTypes from './utils/actionTypes' import isPlainObject from './utils/isPlainObject' // 建立 store 的函數 // preloadedState: store設置的初始值 這個值會覆蓋 Reducer 的默認值 // 若是使用了 combineReducers preloadedState 要和 combineReducers 有相同的keys // enhancer: 中間件 export default function createStore(reducer, preloadedState, enhancer) { // preloadedState能夠不傳 判斷preloadedState是否存在 if (typeof preloadedState === 'function' && typeof enhancer === 'undefined') { enhancer = preloadedState preloadedState = undefined } if (typeof enhancer !== 'undefined') { if (typeof enhancer !== 'function') { throw new Error('Expected the enhancer to be a function.') } // enhancer是一個高階函數 調用enhancer返回一個"增強版"的createStore return enhancer(createStore)(reducer, preloadedState) } if (typeof reducer !== 'function') { throw new Error('Expected the reducer to be a function.') } let currentReducer = reducer let currentState = preloadedState let currentListeners = [] let nextListeners = currentListeners let isDispatching = false // 判斷當前 nextListeners 和 currentListeners 是否爲同一個對象 // 若是是一個對象 就把 nextListeners 改成 currentListeners 的副本 function ensureCanMutateNextListeners() { if (nextListeners === currentListeners) { nextListeners = currentListeners.slice() } } // 獲取當前對象 若是是正在派發action 則不能獲取state function getState() { if (isDispatching) { throw new Error( 'You may not call store.getState() while the reducer is executing. ' + 'The reducer has already received the state as an argument. ' + 'Pass it down from the top reducer instead of reading it from the store.' ) } return currentState } // 訂閱 添加訂閱者 function subscribe(listener) { if (typeof listener !== 'function') { throw new Error('Expected the listener to be a function.') } if (isDispatching) { throw new Error( 'You may not call store.subscribe() while the reducer is executing. ' + 'If you would like to be notified after the store has been updated, subscribe from a ' + 'component and invoke store.getState() in the callback to access the latest state. ' + 'See https://redux.js.org/api-reference/store#subscribe(listener) for more details.' ) } let isSubscribed = true // 每次修改 nextListeners 都要判斷一下 nextListeners 和 currentListeners 是否爲同一個對象 ensureCanMutateNextListeners() // 注意 這裏修改 nextListeners 以後並無改變 currentListeners 而是在下一次用到 currentListeners 纔會改變 nextListeners.push(listener) // 返回一個當前監聽者取消訂閱的方法 return function unsubscribe() { if (!isSubscribed) { return } // 正在派發 action 時不能進行操做 if (isDispatching) { throw new Error( 'You may not unsubscribe from a store listener while the reducer is executing. ' + 'See https://redux.js.org/api-reference/store#subscribe(listener) for more details.' ) } isSubscribed = false ensureCanMutateNextListeners() const index = nextListeners.indexOf(listener) nextListeners.splice(index, 1) } } 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 記錄是否正在 派發action 過程當中不能進行其餘操做 isDispatching = true currentState = currentReducer(currentState, action) } finally { isDispatching = false } // 用到 listeners 纔會修改 currentListeners 以減小修改次數 const listeners = (currentListeners = nextListeners) for (let i = 0; i < listeners.length; i++) { const listener = listeners[i] listener() } return action } // 替換 Reducer 並派發動做 ActionTypes.REPLACE 至關於對state從新進行初始化 function replaceReducer(nextReducer) { if (typeof nextReducer !== 'function') { throw new Error('Expected the nextReducer to be a function.') } currentReducer = nextReducer dispatch({ type: ActionTypes.REPLACE }) } // emmmm...看不懂這個 能夠參考 https://distums.github.io/2017/03/19/observables-proposal-for-ecmascript/ function observable() { const outerSubscribe = subscribe return { subscribe(observer) { if (typeof observer !== 'object' || observer === null) { throw new TypeError('Expected the observer to be an object.') } function observeState() { if (observer.next) { observer.next(getState()) } } observeState() const unsubscribe = outerSubscribe(observeState) return { unsubscribe } }, [$$observable]() { return this } } } dispatch({ type: ActionTypes.INIT }) return { dispatch, subscribe, getState, replaceReducer, [$$observable]: observable } }
bindActionCreators.js
此處參考 《mapStateToProps,mapDispatchToProps的使用姿式》
按註釋上說 這只是一個 convenience method
你能夠把 store.dispatch(MyActionCreators.doSomething()) 換成一個轉成一個函數
咱們使用 action 時 是先經過 actionCreator建立action 而後經過 dispatch 派發出去
經過 bindActionCreator(actionCreator, dispatch) 得到一個能夠直接建立action並派發的函數
bindActionCreators 就是建立一個對象 每一個屬性都是一個 能夠直接建立action並派發的函數
例:
action.increase = (info) => { type:'INCREASE',info } action.decrease = (info) => { type:'DECREASE',info } bindActionCreators({ increase: action.increase, decrease: action.decrease }, dispatch) // 就能夠得到: { increase: (...args) => dispatch(action.increase(...args)), decrease: (...args) => dispatch(action.decrease(...args)) }
源碼:
function bindActionCreator(actionCreator, dispatch) { return function() { return dispatch(actionCreator.apply(this, arguments)) } } /** * 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. */ export default function bindActionCreators(actionCreators, dispatch) { // 若是 actionCreators 是一個函數 說明只有一個 actionCreator if (typeof actionCreators === 'function') { return bindActionCreator(actionCreators, dispatch) } if (typeof actionCreators !== 'object' || actionCreators === 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"?` ) } const keys = Object.keys(actionCreators) const boundActionCreators = {} for (let i = 0; i < keys.length; i++) { const key = keys[i] const actionCreator = actionCreators[key] if (typeof actionCreator === 'function') { boundActionCreators[key] = bindActionCreator(actionCreator, dispatch) } } return boundActionCreators }
applyMiddleware.js
這個應該是最難理解的部分 因此放到最後看>_<
我的理解,這個東西就是在dispatch先後作一些事情=.= 相似koa express的中間件嘛
如下參考 源碼中 redux/docs/advanced/Middleware.md
middleware 在dispatch和action之間提供一個第三方程序擴展點。
如今一步一步理解applyMiddleware在作什麼
首先,假設如今有一個需求,每次dispatch一個action時,都要打印action和state,像下面這樣:
const action = addTodo('Use Redux') console.log('dispatching', action) store.dispatch(action) console.log('next state', store.getState())
可是不可能每一次都這樣打印,也許直接修改dispatch就能夠
const next = store.dispatch store.dispatch = function dispatchAndLog(action) { console.log('dispatching', action) let result = next(action) console.log('next state', store.getState()) return result }
吶,可能不止一個需求,如今我又想記錄錯誤信息了。咱們寫兩個方法,分別給dispatch添加本身想要的功能。
function patchStoreToAddLogging(store) { const next = store.dispatch store.dispatch = function dispatchAndLog(action) { console.log('dispatching', action) let result = next(action) console.log('next state', store.getState()) return result } } function patchStoreToAddCrashReporting(store) { const next = store.dispatch store.dispatch = function dispatchAndReportErrors(action) { try { return next(action) } catch (err) { console.error('Caught an exception!', err) Raven.captureException(err, { extra: { action, state: store.getState() } }) throw err } } } patchStoreToAddLogging(store) patchStoreToAddCrashReporting(store)
可是這樣並很差……很明顯,咱們在修改store的私有屬性了,emmm……這是一個比較hack的方法……要改的優雅一點,把修改dispatch的部分封裝起來。每一次返回新的dispatch,修改store的部分由 applyMiddlewareByMonkeypatching 統一處理。
function logger(store) { const next = store.dispatch // Previously: // store.dispatch = function dispatchAndLog(action) { return function dispatchAndLog(action) { console.log('dispatching', action) let result = next(action) console.log('next state', store.getState()) return result } } function applyMiddlewareByMonkeypatching(store, middlewares) { middlewares = middlewares.slice() middlewares.reverse() // Transform dispatch function with each middleware. middlewares.forEach(middleware => store.dispatch = middleware(store) ) } applyMiddlewareByMonkeypatching(store, [logger, crashReporter])
可是這樣仍是不太好。dispatch是store的私有屬性,咱們卻直接獲取了。思考咱們爲何重寫dispatch,由於咱們在用多箇中間件的時候,第一個中間件修改完dispatch,下一次修改應該是在前一個的基礎之上,包裹上一次修改的dispatch。但其實,這也不是必要的,只要每一次傳入上一次修改後的dispatch就能夠了。
function logger(store) { return function wrapDispatchToAddLogging(next) { return function dispatchAndLog(action) { console.log('dispatching', action) let result = next(action) console.log('next state', store.getState()) return result } } }
這裏的next就是以前的中間件處理後的dispatch,咱們再也不獲取store的私有屬性了,改成用參數傳遞。而後在處理以後(logger(store)(next))返回一個新的dispatch。
爲何這裏要套兩個函數而不是傳入兩個參數(store, next)呢,就至關於把這個函數柯里化了嘛……後面能夠看到用處。
改爲ES6的箭頭函數
const logger = store => next => action => { console.log('dispatching', action) let result = next(action) console.log('next state', store.getState()) return result }
說實話雖然簡潔了,可是看起來一點都不直觀……多是我太菜了。嗯,這就是一箇中間件的寫法了。
能夠簡單的實現下 applyMiddleware
function applyMiddleware(store, middlewares) { middlewares = middlewares.slice() middlewares.reverse() let dispatch = store.dispatch middlewares.forEach(middleware => dispatch = middleware(store)(dispatch) ) return Object.assign({}, store, { dispatch }) }
這樣就能夠最後使用 applyMiddleware
import { createStore, combineReducers, applyMiddleware } from 'redux' const todoApp = combineReducers(reducers) const store = createStore( todoApp, // applyMiddleware() tells createStore() how to handle middleware applyMiddleware(logger, crashReporter) )
深刻(meiyou)的理解以後 開始看applyMiddleware.js源碼
其中用到裏 compose 要先看一下
compose.js
這個是函數式編程的一個……思想?應用?
將函數的嵌套調用寫成組合 compose(b, c, a) 至關於 b(c(a(x)))
export default function compose(...funcs) {
if (funcs.length === 0) { return arg => arg } if (funcs.length === 1) { return funcs[0] } // reduce的參數.. // reduce(function(accumulator, currentValue, currentIndex, array) {...}) return funcs.reduce((a, b) => (...args) => a(b(...args))) } /********** 使用示例 **********/ let a = x => 'a' + x + 'a' let b = x => 'b' + x + 'b' let c = x => 'c' + x + 'c' let foo = compose(b, c, a) console.log(foo('v')) // bcavacb let bar = x => b(c(a(x))) console.log(bar('v')) // bcavacb
最後看applyMiddleware.js
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. */ export default function applyMiddleware(...middlewares) { return createStore => (...args) => { const store = createStore(...args) let dispatch = () => { throw new Error( `Dispatching while constructing your middleware is not allowed. ` + `Other middleware would not be applied to this dispatch.` ) } const middlewareAPI = { getState: store.getState, dispatch: (...args) => dispatch(...args) } const chain = middlewares.map(middleware => middleware(middlewareAPI)) dispatch = compose(...chain)(store.dispatch) return { ...store, dispatch } } }
applyMiddleware([middlewares]) 就是返回一個函數 傳入createStore,返回新的createStore,建立的store的dispatch是通過中間件加工的。
這裏能夠看到編寫中間件嵌套兩個函數的用處,先傳入一個store,只須要再傳入一個最新的dispatch就能夠了,就是把dispatch用中間件輪流處理一下。這裏使用了compose。
勉強看完源碼。僞裝本身理解了這樣子。