redux的源码解析

一、 redux出现的动机

1. Javascript 需要管理比任何时候都要多的state
2. state 在什么时候,由于什么原因,如何变化已然不受控制。
3. 来自前端开发领域的新需求
4. 我们总是将两个难以理清的概念混淆在一起:变化和异步。
5. Redux 视图让state 的变化变得可预测。

二、 核心概念

 1. 想要更新state中的数据,你需要发起一个action,Action就是一个普通的JavaScript 对象用来描述发生了什么。为了把actin 和state串起来开发一些函数,就是redcer。

三、 三大原则

1. 单一数据源 整个应用的state被存储在一棵objecttree中, 并且这个 object tree 只 存在于一个唯一的store 中。

2. state 是只读的,唯一改变state的方法就是触发action,action 是一个用于描述已发 生事件的普通对象。(确保了视图和网络请求不能直接修改state,只能表达想要修改的意图)

3. 使用纯函数来执行修改为了描述action如何改变state tree ,你需要编写reducers。

四、 源码解析

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 中最难理解的一个函数。

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 等。对该方法能够更深的理解。
 */

五、 redux的总结

通过阅读redux的源码,印象最深的就是如何手动写个订阅模式,数据改变的时候,如何触发所有监听事件。闭包的运用登峰造极。其中最难的两个函数 applyMiddlewares 和compose.js 还需要细细体会。没有真正领悟到其精华之处。

谢谢大家。

 

posted @ 2018-05-17 15:02  aGod  阅读(901)  评论(2编辑  收藏  举报