redux的源碼解析

1、 redux出現的動機

1. Javascript 須要管理比任什麼時候候都要多的state
2. state 在何時,因爲什麼緣由,如何變化已然不受控制。
3. 來自前端開發領域的新需求
4. 咱們老是將兩個難以理清的概念混淆在一塊兒:變化和異步。
5. Redux 視圖讓state 的變化變得可預測。前端

2、 核心概念

 1. 想要更新state中的數據,你須要發起一個action,Action就是一個普通的JavaScript 對象用來描述發生了什麼。爲了把actin 和state串起來開發一些函數,就是redcer。react

3、 三大原則

1. 單一數據源 整個應用的state被存儲在一棵objecttree中, 而且這個 object tree 只 存在於一個惟一的store 中。webpack

2. state 是隻讀的,惟一改變state的方法就是觸發action,action 是一個用於描述已發 生事件的普通對象。(確保了視圖和網絡請求不能直接修改state,只能表達想要修改的意圖)git

3. 使用純函數來執行修改成了描述action如何改變state tree ,你須要編寫reducers。github

4、 源碼解析

1. 入口文件index.js
import createStore from './createStore' import combineReducers from './combineReducers' import bindActionCreators from './bindActionCreators' import applyMiddleware from './applyMiddleware' import compose from './compose' import warning from './utils/warning' import __DO_NOT_USE__ActionTypes from './utils/actionTypes'

/* * This is a dummy function to check if the function name has been altered by minification. * If the function has been minified and NODE_ENV !== 'production', warn the user. */

// 判斷文件是否被壓縮了
function isCrushed() {} if ( process.env.NODE_ENV !== 'production' &&
  typeof isCrushed.name === 'string' && isCrushed.name !== 'isCrushed' ) { warning( 'You are currently using minified code outside of NODE_ENV === "production". ' +
      'This means that you are running a slower development build of Redux. ' +
      'You can use loose-envify (https://github.com/zertosh/loose-envify) for browserify ' +
      'or setting mode to production in webpack (https://webpack.js.org/concepts/mode/) ' +
      'to ensure you have the correct code for your production build.' ) } /* 從入口文件能夠看出 redux 對外暴露了5個API。 createStore , combineReducers, bindActionCreators, applyMiddleware, compose, */ export { createStore, combineReducers, bindActionCreators, applyMiddleware, compose, __DO_NOT_USE__ActionTypes }
2. 對外暴露的第一個API createStore => createStore.js
 1 import $$observable from 'symbol-observable'
 2 
 3 import ActionTypes from './utils/actionTypes'
 4 import isPlainObject from './utils/isPlainObject'
 5 
 6 /**  7  * Creates a Redux store that holds the state tree.  8  * The only way to change the data in the store is to call `dispatch()` on it.  9  *  10  * There should only be a single store in your app. To specify how different  11  * parts of the state tree respond to actions, you may combine several reducers  12  * into a single reducer function by using `combineReducers`.  13  *  14  * @param {Function} reducer A function that returns the next state tree, given  15  * the current state tree and the action to handle.  16  *  17  * @param {any} [preloadedState] The initial state. You may optionally specify it  18  * to hydrate the state from the server in universal apps, or to restore a  19  * previously serialized user session.  20  * If you use `combineReducers` to produce the root reducer function, this must be  21  * an object with the same shape as `combineReducers` keys.  22  *  23  * @param {Function} [enhancer] The store enhancer. You may optionally specify it  24  * to enhance the store with third-party capabilities such as middleware,  25  * time travel, persistence, etc. The only store enhancer that ships with Redux  26  * is `applyMiddleware()`.  27  *  28  * @returns {Store} A Redux store that lets you read the state, dispatch actions  29  * and subscribe to changes.  30  */
 31 /*
 32  從源碼上能夠看出 createStore 是一個函數。接收三個參數 reducer, preloadeState, enhancer  33  */
 34 export default function createStore(reducer, preloadedState, enhancer) {  35   // 若是 preloadeState 是一個函數 && enhancer未定義preloadeState 和 enhancer交換位置
 36   if (typeof preloadedState === 'function' && typeof enhancer === 'undefined') {  37     enhancer = preloadedState  38     preloadedState = undefined  39  }  40   //  41   if (typeof enhancer !== 'undefined') {  42     if (typeof enhancer !== 'function') {  43       throw new Error('Expected the enhancer to be a function.')  44  }  45     // 上面兩個判斷是爲了確保 enchancer是個函數
 46     return enhancer(createStore)(reducer, preloadedState)  47  }  48 
 49   // reducer必須是 個函數,若是不是個函數給出友好的 提示
 50   if (typeof reducer !== 'function') {  51     throw new Error('Expected the reducer to be a function.')  52  }  53 
 54   let currentReducer = reducer    // 把reducer 暫存起來
 55   let currentState = preloadedState // 把preloadeState暫存起來
 56   let currentListeners = []  57   let nextListeners = currentListeners  58   let isDispatching = false   //判斷是否正處於dispatch中
 59 
 60   // 若是 nextListeners 和 currrentListeners 都指向一個內存空間的時候, 深複製一份出來。確保兩個之間不會相互影響。
 61   function ensureCanMutateNextListeners() {  62     if (nextListeners === currentListeners) {  63       nextListeners = currentListeners.slice()  64  }  65  }  66 
 67   /**  68  * Reads the state tree managed by the store.  69  *  70  * @returns {any} The current state tree of your application.  71    */
 72   // 獲取目前的 state的值。
 73   function getState() {  74     if (isDispatching) {  75       throw new Error(  76         'You may not call store.getState() while the reducer is executing. ' +
 77           'The reducer has already received the state as an argument. ' +
 78           'Pass it down from the top reducer instead of reading it from the store.'
 79  )  80  }  81 
 82     return currentState  83  }  84 
 85   /**  86  * Adds a change listener. It will be called any time an action is dispatched,  87  * and some part of the state tree may potentially have changed. You may then  88  * call `getState()` to read the current state tree inside the callback.  89  *  90  * You may call `dispatch()` from a change listener, with the following  91  * caveats:  92  *  93  * 1. The subscriptions are snapshotted just before every `dispatch()` call.  94  * If you subscribe or unsubscribe while the listeners are being invoked, this  95  * will not have any effect on the `dispatch()` that is currently in progress.  96  * However, the next `dispatch()` call, whether nested or not, will use a more  97  * recent snapshot of the subscription list.  98  *  99  * 2. The listener should not expect to see all state changes, as the state 100  * might have been updated multiple times during a nested `dispatch()` before 101  * the listener is called. It is, however, guaranteed that all subscribers 102  * registered before the `dispatch()` started will be called with the latest 103  * state by the time it exits. 104  * 105  * @param {Function} listener A callback to be invoked on every dispatch. 106  * @returns {Function} A function to remove this change listener. 107    */
108   // 經典的訂閱函數
109   function subscribe(listener) { 110     if (typeof listener !== 'function') { 111       throw new Error('Expected the listener to be a function.') 112  } 113 
114     if (isDispatching) { 115       throw new Error( 116         'You may not call store.subscribe() while the reducer is executing. ' +
117           'If you would like to be notified after the store has been updated, subscribe from a ' +
118           'component and invoke store.getState() in the callback to access the latest state. ' +
119           'See https://redux.js.org/api-reference/store#subscribe(listener) for more details.'
120  ) 121  } 122     // 閉包的經典應用 每次訂閱一個事件的時候,都有一個內部狀態, 用於後續的取消訂閱
123     let isSubscribed = true
124     // 深複製一份監聽對象
125  ensureCanMutateNextListeners() 126     // 把每一個監聽對象都放置於一個數組中,保存下來,(精華之處,對閉包的使用登峯造極)
127  nextListeners.push(listener) 128     // 當註冊一個監聽事件的返回一個函數,調用這個函數能夠取消訂閱,具體操做方法就是從監聽的數組中移出掉。
129     return function unsubscribe() { 130       // 防止重複取消訂閱
131       if (!isSubscribed) { 132         return
133  } 134 
135       if (isDispatching) { 136         throw new Error( 137           'You may not unsubscribe from a store listener while the reducer is executing. ' +
138             'See https://redux.js.org/api-reference/store#subscribe(listener) for more details.'
139  ) 140  } 141       // 對應上面那條,防止重複取消訂閱
142       isSubscribed = false
143 
144  ensureCanMutateNextListeners() 145       const index = nextListeners.indexOf(listener) 146         // 刪除數組中某一項的方法 splice
147       nextListeners.splice(index, 1) 148  } 149  } 150 
151   /** 152  * Dispatches an action. It is the only way to trigger a state change. 153  * 154  * The `reducer` function, used to create the store, will be called with the 155  * current state tree and the given `action`. Its return value will 156  * be considered the **next** state of the tree, and the change listeners 157  * will be notified. 158  * 159  * The base implementation only supports plain object actions. If you want to 160  * dispatch a Promise, an Observable, a thunk, or something else, you need to 161  * wrap your store creating function into the corresponding middleware. For 162  * example, see the documentation for the `redux-thunk` package. Even the 163  * middleware will eventually dispatch plain object actions using this method. 164  * 165  * @param {Object} action A plain object representing 「what changed」. It is 166  * a good idea to keep actions serializable so you can record and replay user 167  * sessions, or use the time travelling `redux-devtools`. An action must have 168  * a `type` property which may not be `undefined`. It is a good idea to use 169  * string constants for action types. 170  * 171  * @returns {Object} For convenience, the same action object you dispatched. 172  * 173  * Note that, if you use a custom middleware, it may wrap `dispatch()` to 174  * return something else (for example, a Promise you can await). 175    */
176   // 派發一個事件
177   function dispatch(action) { 178     // p、判斷action是不是個對象
179     if (!isPlainObject(action)) { 180       throw new Error( 181         'Actions must be plain objects. ' +
182           'Use custom middleware for async actions.'
183  ) 184  } 185     // 嚴格控制 action 的書寫格式 { type: 'INCREMENT'}
186     if (typeof action.type === 'undefined') { 187       throw new Error( 188         'Actions may not have an undefined "type" property. ' +
189           'Have you misspelled a constant?'
190  ) 191  } 192 
193     if (isDispatching) { 194       throw new Error('Reducers may not dispatch actions.') 195  } 196     // isDipatching 也是閉包的經典用法
197       /*
198  觸發 dispatch的時候 把 isDispatching 改成 true。 照應全篇中對 dispatching 正在觸發的時候的判斷 199  finally 執行完畢的時候 置爲 false 200        */
201     try { 202       isDispatching = true
203       // 獲取最新 的state 值。 currentState 經典之處閉包
204       currentState = currentReducer(currentState, action) 205     } finally { 206       isDispatching = false
207  } 208 
209     // 對監聽對象重新賦值 其實裏面每一個listener都是一個函數。 於subscribe相對應
210     // 當每發生一個dispatch 事件的時候, 都循環調用,觸發監聽事件
211     const listeners = (currentListeners = nextListeners) 212     for (let i = 0; i < listeners.length; i++) { 213       const listener = listeners[i] 214  listener() 215  } 216 
217     return action 218  } 219 
220   /** 221  * Replaces the reducer currently used by the store to calculate the state. 222  * 223  * You might need this if your app implements code splitting and you want to 224  * load some of the reducers dynamically. You might also need this if you 225  * implement a hot reloading mechanism for Redux. 226  * 227  * @param {Function} nextReducer The reducer for the store to use instead. 228  * @returns {void} 229    */
230   // 替換 reducer 用 新的 reducer替換之前的reducer 參數一樣必須是函數
231   function replaceReducer(nextReducer) { 232     if (typeof nextReducer !== 'function') { 233       throw new Error('Expected the nextReducer to be a function.') 234  } 235 
236     currentReducer = nextReducer 237  dispatch({ type: ActionTypes.REPLACE }) 238  } 239 
240   /** 241  * Interoperability point for observable/reactive libraries. 242  * @returns {observable} A minimal observable of state changes. 243  * For more information, see the observable proposal: 244  * https://github.com/tc39/proposal-observable 245    */
246   // 觀察模式
247   function observable() { 248     const outerSubscribe = subscribe 249     return { 250       /** 251  * The minimal observable subscription method. 252  * @param {Object} observer Any object that can be used as an observer. 253  * The observer object should have a `next` method. 254  * @returns {subscription} An object with an `unsubscribe` method that can 255  * be used to unsubscribe the observable from the store, and prevent further 256  * emission of values from the observable. 257        */
258  subscribe(observer) { 259         if (typeof observer !== 'object' || observer === null) { 260           throw new TypeError('Expected the observer to be an object.') 261  } 262 
263         function observeState() { 264           if (observer.next) { 265  observer.next(getState()) 266  } 267  } 268 
269  observeState() 270         const unsubscribe = outerSubscribe(observeState) 271         return { unsubscribe } 272  }, 273 
274  [$$observable]() { 275         return this
276  } 277  } 278  } 279 
280   // When a store is created, an "INIT" action is dispatched so that every
281   // reducer returns their initial state. This effectively populates
282   // the initial state tree.
283     // 觸發一state 樹
284  dispatch({ type: ActionTypes.INIT }) 285   /*
286  由此能夠看出 調用 createStore(); 後。對外暴露的方法 287  1. dispatch 288  2. subscribe 289  3. getState 290  4. replaceReducer 291  5.觀察模式 292  const store = createStore(reducer, preloadedState, enchancer); 293  store.dispatch(action); 294  store.getState(); // 爲何這個方法可以得到 state的值。由於 currentState 的閉包實現。 295  store.subscribe(() => console.log(store.getState())); 296  store.replaceReducer(reducer); 297  總結:縱觀createStore方法的實現,其實都是創建在閉包的基礎之上。可謂是把閉包用到了極致。 298    */
299   return { 300  dispatch, 301  subscribe, 302  getState, 303  replaceReducer, 304  [$$observable]: observable 305  } 306 }
3. combineReducers.js
 1 import ActionTypes from './utils/actionTypes'
 2 import warning from './utils/warning'
 3 import isPlainObject from './utils/isPlainObject'
 4 
 5 function getUndefinedStateErrorMessage(key, action) {  6   const actionType = action && action.type  7   const actionDescription =
 8     (actionType && `action "${String(actionType)}"`) || 'an action'
 9 
 10   return (  11     `Given ${actionDescription}, reducer "${key}" returned undefined. ` +
 12     `To ignore an action, you must explicitly return the previous state. ` +
 13     `If you want this reducer to hold no value, you can return null instead of undefined.`  14  )  15 }  16 
 17 function getUnexpectedStateShapeWarningMessage(  18  inputState,  19  reducers,  20  action,  21  unexpectedKeyCache  22 ) {  23   const reducerKeys = Object.keys(reducers)  24   const argumentName =
 25     action && action.type === ActionTypes.INIT  26       ? 'preloadedState argument passed to createStore'
 27       : 'previous state received by the reducer'
 28 
 29   if (reducerKeys.length === 0) {  30     return (  31       'Store does not have a valid reducer. Make sure the argument passed ' +
 32       'to combineReducers is an object whose values are reducers.'
 33  )  34  }  35 
 36   if (!isPlainObject(inputState)) {  37     return (  38       `The ${argumentName} has unexpected type of "` +  39  {}.toString.call(inputState).match(/\s([a-z|A-Z]+)/)[1] +  40       `". Expected argument to be an object with the following ` +
 41       `keys: "${reducerKeys.join('", "')}"`  42  )  43  }  44 
 45   const unexpectedKeys = Object.keys(inputState).filter(  46     key => !reducers.hasOwnProperty(key) && !unexpectedKeyCache[key]  47  )  48 
 49   unexpectedKeys.forEach(key => {  50     unexpectedKeyCache[key] = true
 51  })  52 
 53   if (action && action.type === ActionTypes.REPLACE) return
 54 
 55   if (unexpectedKeys.length > 0) {  56     return (  57       `Unexpected ${unexpectedKeys.length > 1 ? 'keys' : 'key'} ` +
 58       `"${unexpectedKeys.join('", "')}" found in ${argumentName}. ` +
 59       `Expected to find one of the known reducer keys instead: ` +
 60       `"${reducerKeys.join('", "')}". Unexpected keys will be ignored.`  61  )  62  }  63 }  64 
 65 function assertReducerShape(reducers) {  66   Object.keys(reducers).forEach(key => {  67     const reducer = reducers[key]  68     const initialState = reducer(undefined, { type: ActionTypes.INIT })  69 
 70     if (typeof initialState === 'undefined') {  71       throw new Error(  72         `Reducer "${key}" returned undefined during initialization. ` +
 73           `If the state passed to the reducer is undefined, you must ` +
 74           `explicitly return the initial state. The initial state may ` +
 75           `not be undefined. If you don't want to set a value for this reducer, ` +  76  `you can use null instead of undefined.`  77  )  78  }  79 
 80  if (  81  typeof reducer(undefined, {  82  type: ActionTypes.PROBE_UNKNOWN_ACTION()  83       }) === 'undefined'
 84  ) {  85  throw new Error(  86  `Reducer "${key}" returned undefined when probed with a random type. ` +  87           `Don't try to handle ${  88  ActionTypes.INIT  89           } or other actions in "redux/*" ` +
 90           `namespace. They are considered private. Instead, you must return the ` +
 91           `current state for any unknown actions, unless it is undefined, ` +
 92           `in which case you must return the initial state, regardless of the ` +
 93           `action type. The initial state may not be undefined, but can be null.`  94  )  95  }  96  })  97 }  98 
 99 /** 100  * Turns an object whose values are different reducer functions, into a single 101  * reducer function. It will call every child reducer, and gather their results 102  * into a single state object, whose keys correspond to the keys of the passed 103  * reducer functions. 104  * 105  * @param {Object} reducers An object whose values correspond to different 106  * reducer functions that need to be combined into one. One handy way to obtain 107  * it is to use ES6 `import * as reducers` syntax. The reducers may never return 108  * undefined for any action. Instead, they should return their initial state 109  * if the state passed to them was undefined, and the current state for any 110  * unrecognized action. 111  * 112  * @returns {Function} A reducer function that invokes every reducer inside the 113  * passed object, and builds a state object with the same shape. 114  */
115 
116 /*
117  combineReducers 顧名思義就是合併reduces的一個方法。 118  1. 爲了項目便於維護與管理咱們就須要拆按模塊拆分reducers。 119  2. 而combineReducers就是爲了解決這個的問題的。 120 
121  */
122 export default function combineReducers(reducers) { // 參數reducers 是一個對象
123   const reducerKeys = Object.keys(reducers) // 獲取reducers的k
124   const finalReducers = {} 125   for (let i = 0; i < reducerKeys.length; i++) { 126     const key = reducerKeys[i] 127 
128     if (process.env.NODE_ENV !== 'production') { 129       if (typeof reducers[key] === 'undefined') { 130         warning(`No reducer provided for key "${key}"`) 131  } 132  } 133 
134     // 深複製一份reducers出來, 防止後續操做出現不可控因素
135     if (typeof reducers[key] === 'function') { 136       finalReducers[key] = reducers[key] 137  } 138  } 139   const finalReducerKeys = Object.keys(finalReducers) 140 
141  let unexpectedKeyCache 142   if (process.env.NODE_ENV !== 'production') { 143     unexpectedKeyCache = {} 144  } 145 
146  let shapeAssertionError 147   try { 148  assertReducerShape(finalReducers) 149   } catch (e) { 150     shapeAssertionError = e 151  } 152   // 閉包的運用, 把合併的 reducer保存下來。
153   return function combination(state = {}, action) { 154     if (shapeAssertionError) { 155       throw shapeAssertionError 156  } 157 
158     if (process.env.NODE_ENV !== 'production') { 159       const warningMessage = getUnexpectedStateShapeWarningMessage( 160  state, 161  finalReducers, 162  action, 163  unexpectedKeyCache 164  ) 165       if (warningMessage) { 166  warning(warningMessage) 167  } 168  } 169 
170     let hasChanged = false
171     const nextState = {} 172     for (let i = 0; i < finalReducerKeys.length; i++) { 173       const key = finalReducerKeys[i] 174       const reducer = finalReducers[key] 175       const previousStateForKey = state[key] 176       // 把合併的時候的key值做爲Key值爲標準。 在循環遍歷的時候取出對應的 reducers 觸發 reducer函數。
177       /*
178  其實對應的createStore.js中的 179  try { 180  isDispatching = true 181  // 獲取最新 的state 值。 currentState 經典之處閉包 182  currentState = currentReducer(currentState, action) 183  } finally { 184  isDispatching = false 185  } 186        */
187       const nextStateForKey = reducer(previousStateForKey, action) 188       if (typeof nextStateForKey === 'undefined') { 189         const errorMessage = getUndefinedStateErrorMessage(key, action) 190         throw new Error(errorMessage) 191  } 192       nextState[key] = nextStateForKey 193       hasChanged = hasChanged || nextStateForKey !== previousStateForKey 194  } 195     return hasChanged ? nextState : state 196  } 197 } 198 /** 199  * 使用方法 200  * const reducers = combineReducers({ reducer1, reducer2 }); 201  * const store = createStore(reducers, preloadedState, enchancer); 202  */
4. bindActionCreators.js
 1 // 主要這個函數
 2 function bindActionCreator(actionCreator, dispatch) {  3   return function() {  4     return dispatch(actionCreator.apply(this, arguments))  5  }  6 }  7 
 8 /**  9  * Turns an object whose values are action creators, into an object with the 10  * same keys, but with every function wrapped into a `dispatch` call so they 11  * may be invoked directly. This is just a convenience method, as you can call 12  * `store.dispatch(MyActionCreators.doSomething())` yourself just fine. 13  * 14  * For convenience, you can also pass a single function as the first argument, 15  * and get a function in return. 16  * 17  * @param {Function|Object} actionCreators An object whose values are action 18  * creator functions. One handy way to obtain it is to use ES6 `import * as` 19  * syntax. You may also pass a single function. 20  * 21  * @param {Function} dispatch The `dispatch` function available on your Redux 22  * store. 23  * 24  * @returns {Function|Object} The object mimicking the original object, but with 25  * every action creator wrapped into the `dispatch` call. If you passed a 26  * function as `actionCreators`, the return value will also be a single 27  * function. 28  */
29 
30 /*
31  接受兩個參數,一個action creator, 一個是 value的 action creator的對象。 32  dispatch 。 一個由 Store 實列 提供的dispatch的函數。 看createStore.js源碼就能夠作知道其中原理。 33  */
34 export default function bindActionCreators(actionCreators, dispatch) { 35   if (typeof actionCreators === 'function') { 36     return bindActionCreator(actionCreators, dispatch) 37  } 38 
39   if (typeof actionCreators !== 'object' || actionCreators === null) { 40     throw new Error( 41       `bindActionCreators expected an object or a function, instead received ${ 42         actionCreators === null ? 'null' : typeof actionCreators 43       }. ` +
44         `Did you write "import ActionCreators from" instead of "import * as ActionCreators from"?` 45  ) 46  } 47 
48   const keys = Object.keys(actionCreators) 49   const boundActionCreators = {} 50   for (let i = 0; i < keys.length; i++) { 51     const key = keys[i] 52     const actionCreator = actionCreators[key] 53     if (typeof actionCreator === 'function') { 54       boundActionCreators[key] = bindActionCreator(actionCreator, dispatch) 55  } 56  } 57   /*
58  一個與原對象相似的對象,只不過這個對象的 value 都是會直接 dispatch 原 action creator 返回的結果的函數。 59  若是傳入一個單獨的函數做爲 actionCreators,那麼返回的結果也是一個單獨的函數。 60  原本觸發 action 的方法是 store.dispatch(action); 61  通過這個方法封裝後 能夠直接調用函數名字 62  aa('參數'); 63    */
64   return boundActionCreators 65 }

5. applyMiddleware.js  在redux 中最難理解的一個函數。web

import compose from './compose' import createStore from "./createStore"; /** * 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返回一個高階函數。 /* applyMiddleware的使用地方 const store = createStore(reducer,{}, applyMiddleware(...middlewares)); 由此能夠看出 applyMiddlewares 的使用方法主要是和 createStore.js 中 createStore方法的第三個參數對應。翻開源碼 if (typeof enhancer !== 'undefined') { if (typeof enhancer !== 'function') { throw new Error('Expected the enhancer to be a function.') } // 上面兩個判斷是爲了確保 enchancer是個函數 return enhancer(createStore)(reducer, preloadedState) } */ export default function applyMiddleware(...middlewares) { // createSotre 中 的第三個參數 enhancer
  return createStore => (...args) => { // 經過對應的代碼能夠發現其實 ...aregs 對應的是 reducer, preloadedState
    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.` ) } // 定義中間件必須知足的條件。 API getState, dispatch();
    const middlewareAPI = { getState: store.getState, dispatch: (...args) => dispatch(...args) } const chain = middlewares.map(middleware => middleware(middlewareAPI)) /** * export default function compose(...funcs) { if (funcs.length === 0) { return arg => arg } if (funcs.length === 1) { return funcs[0] } return funcs.reduce((a, b) => (...args) => a(b(...args))) } compose函數 主要是 利用數組 reducer 方法對參數的處理。 */ dispatch = compose(...chain)(store.dispatch) // 經過這個返回值咱們能夠知道 在createStore.js中enchancer的返回值。
    return { ...store, dispatch } } } /** * applyMiddlewares函數比較難理解。 多看幾個中間件,好比 logger 和 redux-thunk 等。對該方法可以更深的理解。 */

5、 redux的總結

經過閱讀redux的源碼,印象最深的就是如何手動寫個訂閱模式,數據改變的時候,如何觸發全部監聽事件。閉包的運用登峯造極。其中最難的兩個函數 applyMiddlewares 和compose.js 還須要細細體會。沒有真正領悟到其精華之處。express

謝謝你們。redux

相關文章
相關標籤/搜索