欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  IT编程

redux的源码解析

程序员文章站 2024-01-03 23:52:10
一、 redux出现的动机 1. Javascript 需要管理比任何时候都要多的state2. state 在什么时候,由于什么原因,如何变化已然不受控制。3. 来自前端开发领域的新需求4. 我们总是将两个难以理清的概念混淆在一起:变化和异步。5. Redux 视图让state 的变化变得可预测。 ......

一、 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 还需要细细体会。没有真正领悟到其精华之处。

谢谢大家。

 

上一篇:

下一篇: