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 还需要细细体会。没有真正领悟到其精华之处。
谢谢大家。
推荐阅读
-
redux的源码解析
-
chrome app无法显示php解析后的html内容
-
小程序的四次元口袋:editor富文本编辑器的使用、渲染,以及rich-text进行解析
-
神箭手云爬虫-爬取携程【国际】航班/机票信息-利用python解析返回的json文件将信息存储进Mysql数据库
-
荐 Spring源码之容器的基本实现:通过Xml配置装载Bean
-
在研究基于wordpress的网站源码,始终不懂这种页面跳转是咋一回事?解决思路
-
SpringIOC源码解析1(基于xml配置)
-
PHP程序中的文件锁、互斥锁、读写锁使用技巧解析_PHP
-
Redux与它的中间件:redux-thunk,redux-actions,redux-promise,redux-sage
-
解析用PHP实现var_export的详细介绍_PHP教程