正式学习React(四) ----Redux源码分析
发布日期:2021-08-31 03:56:19 浏览次数:11 分类:技术文章

本文共 21855 字,大约阅读时间需要 72 分钟。

今天看了下Redux的源码,竟然出奇的简单,好吧。简单翻译做下笔记:

喜欢的同学自己可以去github上看:

createStore.js

 

1 import isPlainObject from 'lodash/isPlainObject'  2 import $$observable from 'symbol-observable'  3   4 /**  5  * These are private action types reserved by Redux.  6  * For any unknown actions, you must return the current state.  7  * If the current state is undefined, you must return the initial state.  8  * Do not reference these action types directly in your code.  9  */ 10 export var ActionTypes = { 11   INIT: '@@redux/INIT' 12 } 13  14 /** 15  * Creates a Redux store that holds the state tree. 16  * The only way to change the data in the store is to call `dispatch()` on it. 17  * 18  * There should only be a single store in your app. To specify how different 19  * parts of the state tree respond to actions, you may combine several reducers 20  * into a single reducer function by using `combineReducers`. 21  * 22  * @param {Function} reducer A function that returns the next state tree, given 23  * the current state tree and the action to handle. 24  * 25  * @param {any} [preloadedState] The initial state. You may optionally specify it 26  * to hydrate the state from the server in universal apps, or to restore a 27  * previously serialized user session. 28  * If you use `combineReducers` to produce the root reducer function, this must be 29  * an object with the same shape as `combineReducers` keys. 30  * 31  * @param {Function} [enhancer] The store enhancer. You may optionally specify it 32  * to enhance the store with third-party capabilities such as middleware, 33  * time travel, persistence, etc. The only store enhancer that ships with Redux 34  * is `applyMiddleware()`. 35  * 36  * @returns {Store} A Redux store that lets you read the state, dispatch actions 37  * and subscribe to changes. 38  */ 39 export default function createStore(reducer, preloadedState, enhancer) {
//如果没有提供初始的state,提供了enhancer,就将初试state置为undefined 40 if (typeof preloadedState === 'function' && typeof enhancer === 'undefined') { 41 enhancer = preloadedState 42 preloadedState = undefined 43 } 44 //确保enhancer一定是函数 45 if (typeof enhancer !== 'undefined') { 46 if (typeof enhancer !== 'function') { 47 throw new Error('Expected the enhancer to be a function.') 48 } 49 //这个会返回一个store,只不过是增强版的。 50 return enhancer(createStore)(reducer, preloadedState) 51 } 52 53 if (typeof reducer !== 'function') { 54 throw new Error('Expected the reducer to be a function.') 55 } 56 //初始化一些变量,用作闭包。 57 var currentReducer = reducer 58 var currentState = preloadedState //undefined || 传进来的初始值 59 var currentListeners = [] 60 var nextListeners = currentListeners 61 var isDispatching = false 62 //这个辅助函数是这样的,如果你没有调用dispacth,那么每次调用subscribe来添加监听器的都会被push到nextListenrs,他是currentListerns的一个副本。 //总的来说这个函数的意义我个人觉得就是保护了currentListeners不被随意污染.保证每次dispacth前状态不变。 63 function ensureCanMutateNextListeners() { 64 if (nextListeners === currentListeners) { 65 nextListeners = currentListeners.slice() 66 } 67 } 68 69 /** 70 * Reads the state tree managed by the store. 71 * 72 * @returns {any} The current state tree of your application. 73 */ //这个就是返回当前的state 74 function getState() { 75 return currentState 76 } 77 78 /** 79 * Adds a change listener. It will be called any time an action is dispatched, 80 * and some part of the state tree may potentially have changed. You may then 81 * call `getState()` to read the current state tree inside the callback. 82 * 83 * You may call `dispatch()` from a change listener, with the following 84 * caveats: 85 * 86 * 1. The subscriptions are snapshotted just before every `dispatch()` call. 87 * If you subscribe or unsubscribe while the listeners are being invoked, this 88 * will not have any effect on the `dispatch()` that is currently in progress. 89 * However, the next `dispatch()` call, whether nested or not, will use a more 90 * recent snapshot of the subscription list. 91 * 92 * 2. The listener should not expect to see all state changes, as the state 93 * might have been updated multiple times during a nested `dispatch()` before 94 * the listener is called. It is, however, guaranteed that all subscribers 95 * registered before the `dispatch()` started will be called with the latest 96 * state by the time it exits. 97 * 98 * @param {Function} listener A callback to be invoked on every dispatch. 99 * @returns {Function} A function to remove this change listener.100 */ //简单点说就是 设置监听函数,每次dispatch(action)的时候,这些传进去的listenr们就会全部被调用101 function subscribe(listener) {102 if (typeof listener !== 'function') {103 throw new Error('Expected listener to be a function.')104 }105 106 var isSubscribed = true107 108 ensureCanMutateNextListeners()109 nextListeners.push(listener)110 111 return function unsubscribe() {112 if (!isSubscribed) {113 return114 }115 116 isSubscribed = false117 118 ensureCanMutateNextListeners()119 var index = nextListeners.indexOf(listener)120 nextListeners.splice(index, 1)121 }122 }123 124 /**125 * Dispatches an action. It is the only way to trigger a state change.126 *127 * The `reducer` function, used to create the store, will be called with the128 * current state tree and the given `action`. Its return value will129 * be considered the **next** state of the tree, and the change listeners130 * will be notified.131 *132 * The base implementation only supports plain object actions. If you want to133 * dispatch a Promise, an Observable, a thunk, or something else, you need to134 * wrap your store creating function into the corresponding middleware. For135 * example, see the documentation for the `redux-thunk` package. Even the136 * middleware will eventually dispatch plain object actions using this method.137 *138 * @param {Object} action A plain object representing “what changed”. It is139 * a good idea to keep actions serializable so you can record and replay user140 * sessions, or use the time travelling `redux-devtools`. An action must have141 * a `type` property which may not be `undefined`. It is a good idea to use142 * string constants for action types.143 *144 * @returns {Object} For convenience, the same action object you dispatched.145 *146 * Note that, if you use a custom middleware, it may wrap `dispatch()` to147 * return something else (for example, a Promise you can await).148 */ //通知store,我要更新state了。149 function dispatch(action) {150 if (!isPlainObject(action)) {151 throw new Error(152 'Actions must be plain objects. ' +153 'Use custom middleware for async actions.'154 )155 }156 157 if (typeof action.type === 'undefined') {158 throw new Error(159 'Actions may not have an undefined "type" property. ' +160 'Have you misspelled a constant?'161 )162 }163 164 if (isDispatching) {165 throw new Error('Reducers may not dispatch actions.')166 }167 168 try {169 isDispatching = true170 currentState = currentReducer(currentState, action)171 } finally {172 isDispatching = false173 }174 175 var listeners = currentListeners = nextListeners176 for (var i = 0; i < listeners.length; i++) {177 var listener = listeners[i]178 listener()179 }180 181 return action182 }183 184 /**185 * Replaces the reducer currently used by the store to calculate the state.186 *187 * You might need this if your app implements code splitting and you want to188 * load some of the reducers dynamically. You might also need this if you189 * implement a hot reloading mechanism for Redux.190 *191 * @param {Function} nextReducer The reducer for the store to use instead.192 * @returns {void}193 */ //重置reducer,然后初始化state194 function replaceReducer(nextReducer) {195 if (typeof nextReducer !== 'function') {196 throw new Error('Expected the nextReducer to be a function.')197 }198 199 currentReducer = nextReducer200 dispatch({ type: ActionTypes.INIT })201 }202 203 /**204 * Interoperability point for observable/reactive libraries.205 * @returns {observable} A minimal observable of state changes.206 * For more information, see the observable proposal:207 * https://github.com/zenparsing/es-observable208 */ //暂时我还不知道这个有什么吊用,先不管好了。209 function observable() {210 var outerSubscribe = subscribe211 return {212 /**213 * The minimal observable subscription method.214 * @param {Object} observer Any object that can be used as an observer.215 * The observer object should have a `next` method.216 * @returns {subscription} An object with an `unsubscribe` method that can217 * be used to unsubscribe the observable from the store, and prevent further218 * emission of values from the observable.219 */220 subscribe(observer) {221 if (typeof observer !== 'object') {222 throw new TypeError('Expected the observer to be an object.')223 }224 225 function observeState() {226 if (observer.next) {227 observer.next(getState())228 }229 }230 231 observeState()232 var unsubscribe = outerSubscribe(observeState)233 return { unsubscribe }234 },235 236 [$$observable]() {237 return this238 }239 }240 }241 242 // When a store is created, an "INIT" action is dispatched so that every243 // reducer returns their initial state. This effectively populates244 // the initial state tree. //给currentState设定初始状态245 dispatch({ type: ActionTypes.INIT })246 247 return {248 dispatch,249 subscribe,250 getState,251 replaceReducer,252 [$$observable]: observable253 }254 }

关于 createStore,我们就关注它返回的对象,subscribe是订阅监听函数的,getState是返回state的,dispacth是发布消息的,更新state的。

剩下那2个就不管他算了。

 

combineReducers.js

1 import { ActionTypes } from './createStore'  2 import isPlainObject from 'lodash/isPlainObject'  3 import warning from './utils/warning'  4   5 var NODE_ENV = typeof process !== 'undefined' ? process.env.NODE_ENV : 'development'  6   7 function getUndefinedStateErrorMessage(key, action) {  8   var actionType = action && action.type  9   var actionName = actionType && `"${actionType.toString()}"` || 'an action' 10  11   return ( 12     `Given action ${actionName}, reducer "${key}" returned undefined. ` + 13     `To ignore an action, you must explicitly return the previous state.` 14   ) 15 } 16  17 function getUnexpectedStateShapeWarningMessage(inputState, reducers, action, unexpectedKeyCache) { 18   var reducerKeys = Object.keys(reducers) 19   var argumentName = action && action.type === ActionTypes.INIT ? 20     'preloadedState argument passed to createStore' : 21     'previous state received by the reducer' 22  23   if (reducerKeys.length === 0) { 24     return ( 25       'Store does not have a valid reducer. Make sure the argument passed ' + 26       'to combineReducers is an object whose values are reducers.' 27     ) 28   } 29  30   if (!isPlainObject(inputState)) { 31     return ( 32       `The ${argumentName} has unexpected type of "` + 33       ({}).toString.call(inputState).match(/\s([a-z|A-Z]+)/)[1] + 34       `". Expected argument to be an object with the following ` + 35       `keys: "${reducerKeys.join('", "')}"` 36     ) 37   } 38  39   var unexpectedKeys = Object.keys(inputState).filter(key => 40     !reducers.hasOwnProperty(key) && 41     !unexpectedKeyCache[key] 42   ) 43  44   unexpectedKeys.forEach(key => { 45     unexpectedKeyCache[key] = true 46   }) 47  48   if (unexpectedKeys.length > 0) { 49     return ( 50       `Unexpected ${unexpectedKeys.length > 1 ? 'keys' : 'key'} ` + 51       `"${unexpectedKeys.join('", "')}" found in ${argumentName}. ` + 52       `Expected to find one of the known reducer keys instead: ` + 53       `"${reducerKeys.join('", "')}". Unexpected keys will be ignored.` 54     ) 55   } 56 } 57  58 function assertReducerSanity(reducers) { 59   Object.keys(reducers).forEach(key => { 60     var reducer = reducers[key] 61     var initialState = reducer(undefined, { type: ActionTypes.INIT }) 62  63     if (typeof initialState === 'undefined') { 64       throw new Error( 65         `Reducer "${key}" returned undefined during initialization. ` + 66         `If the state passed to the reducer is undefined, you must ` + 67         `explicitly return the initial state. The initial state may ` + 68         `not be undefined.` 69       ) 70     } 71  72     var type = '@@redux/PROBE_UNKNOWN_ACTION_' + Math.random().toString(36).substring(7).split('').join('.') 73     if (typeof reducer(undefined, { type }) === 'undefined') { 74       throw new Error( 75         `Reducer "${key}" returned undefined when probed with a random type. ` + 76         `Don't try to handle ${ActionTypes.INIT} or other actions in "redux/*" ` + 77         `namespace. They are considered private. Instead, you must return the ` + 78         `current state for any unknown actions, unless it is undefined, ` + 79         `in which case you must return the initial state, regardless of the ` + 80         `action type. The initial state may not be undefined.` 81       ) 82     } 83   }) 84 } 85  86 /** 87  * Turns an object whose values are different reducer functions, into a single 88  * reducer function. It will call every child reducer, and gather their results 89  * into a single state object, whose keys correspond to the keys of the passed 90  * reducer functions. 91  * 92  * @param {Object} reducers An object whose values correspond to different 93  * reducer functions that need to be combined into one. One handy way to obtain 94  * it is to use ES6 `import * as reducers` syntax. The reducers may never return 95  * undefined for any action. Instead, they should return their initial state 96  * if the state passed to them was undefined, and the current state for any 97  * unrecognized action. 98  * 99  * @returns {Function} A reducer function that invokes every reducer inside the100  * passed object, and builds a state object with the same shape.101  */102 export default function combineReducers(reducers) {103   var reducerKeys = Object.keys(reducers)104   var finalReducers = {} 105   for (var i = 0; i < reducerKeys.length; i++) {106     var key = reducerKeys[i]107 108     if (NODE_ENV !== 'production') {
//排除undefined.109 if (typeof reducers[key] === 'undefined') {110 warning(`No reducer provided for key "${key}"`)111 }112 }113 //排除不是func的114 if (typeof reducers[key] === 'function') {115 finalReducers[key] = reducers[key]116 }117 }118 var finalReducerKeys = Object.keys(finalReducers)119 120 if (NODE_ENV !== 'production') {121 var unexpectedKeyCache = {}122 }123 124 var sanityError125 try {
//这里会对每个子reducer的state进行检查。返回不能为undefined126 assertReducerSanity(finalReducers)127 } catch (e) {128 sanityError = e129 }130 //我们最最关心的就是这个返回函数,其实我们如果已经堆这个函数有一定的了解,就知道这个函数其实就把子reducers全部在这个函数里执行一边, //返回最后的state131 return function combination(state = {}, action) {132 if (sanityError) {133 throw sanityError134 }135 136 if (NODE_ENV !== 'production') {137 var warningMessage = getUnexpectedStateShapeWarningMessage(state, finalReducers, action, unexpectedKeyCache)138 if (warningMessage) {139 warning(warningMessage)140 }141 }142 143 var hasChanged = false144 var nextState = {}145 for (var i = 0; i < finalReducerKeys.length; i++) {146 var key = finalReducerKeys[i]147 var reducer = finalReducers[key]148 var previousStateForKey = state[key]149 var nextStateForKey = reducer(previousStateForKey, action)150 if (typeof nextStateForKey === 'undefined') {151 var errorMessage = getUndefinedStateErrorMessage(key, action)152 throw new Error(errorMessage)153 }154 nextState[key] = nextStateForKey155 hasChanged = hasChanged || nextStateForKey !== previousStateForKey156 }157 return hasChanged ? nextState : state158 }159 }

compose.js

1 /** 2  * Composes single-argument functions from right to left. The rightmost 3  * function can take multiple arguments as it provides the signature for 4  * the resulting composite function. 5  * 6  * @param {...Function} funcs The functions to compose. 7  * @returns {Function} A function obtained by composing the argument functions 8  * from right to left. For example, compose(f, g, h) is identical to doing 9  * (...args) => f(g(h(...args))).10  */11 12 export default function compose(...funcs) {13     14      //funcs就是我们传入的中间件,fn1,fn2...15      //如果什么都没有,就返回一个function(arg){return arg};当作默认的中间件16   if (funcs.length === 0) {17     return arg => arg18   }19 20 21      //排除所有中间件参数中不是function的22   funcs = funcs.filter(func => typeof func === 'function')23 24 25       //如果只有一个中间件参数,就把这个唯一的中间件返回。26   if (funcs.length === 1) {27     return funcs[0]28   }29 30      //如果中间件参数个数超过1个31 32      //取出最后一个中间件参数33   const last = funcs[funcs.length - 1]34 35      //将funcs中最开头到倒数第二个的数组复制一份,即排除了最后一个中间件36   const rest = funcs.slice(0, -1)37 38 39   //返回(...args) => f(g(h(...args))).40   return (...args) => rest.reduceRight((composed, f) => f(composed), last(...args))41 }

 

解释完compose.js,我们可以简单的理解为: compose就是用来将函数组合起来使用。

假如:compose(f, g, h) 分装后:(...args) => f(g(h(...args))).

 

 

 

applyMiddleware.js

1 import compose from './compose' 2  3 /** 4  * Creates a store enhancer that applies middleware to the dispatch method 5  * of the Redux store. This is handy for a variety of tasks, such as expressing 6  * asynchronous actions in a concise manner, or logging every action payload. 7  * 8  * See `redux-thunk` package as an example of the Redux middleware. 9  *10  * Because middleware is potentially asynchronous, this should be the first11  * store enhancer in the composition chain.12  *13  * Note that each middleware will be given the `dispatch` and `getState` functions14  * as named arguments.15  *16  * @param {...Function} middlewares The middleware chain to be applied.17  * @returns {Function} A store enhancer applying the middleware.18  */19 export default function applyMiddleware(...middlewares) {
//短短几行,很简单哟。 //返回我们在creteStore里看到的enhancer
20   return (createStore) => (reducer, preloadedState, enhancer) => {21     var store = createStore(reducer, preloadedState, enhancer)22     var dispatch = store.dispatch23     var chain = []24 25     var middlewareAPI = {26       getState: store.getState,27       dispatch: (action) => dispatch(action)28     }  //将 当前store的getState和dispacth方法挂载到中间件里。29     chain = middlewares.map(middleware => middleware(middlewareAPI))30     dispatch = compose(...chain)(store.dispatch)31 32     return {33       ...store,34       dispatch35     }36   }37 }

 假如我们在代码里看到:

const store =  applyMiddleware(promise, thunk, observable)(createStore)(reducer); 亦或是
const store = createStore(
reducer,state,applyMiddleware(promise, thunk, observable)
);
我们根据上面的代码走一遍,看看是怎么个执行过程。
1: applyMiddleware(promise, thunk, observable) 会返回enhancer.
2:enhancer(createStore) 返回一个将中间件参数都闭包进来的 createStore函数,此时我们叫他加强版!我给他标注是红色; 3:createStore(reducer) 然后就是上面代码的21-34行。 最后我们关心一下这个返回的store里的dispatch,其实是加强版的,因为里面是执行中间件的!!但是中间件里面到底干了什么, 我们目前不得而知,总之记住,dispacth现在牛逼了就行。比如可以处理异步啦等等。
bindActionCreators.js
1 function bindActionCreator(actionCreator, dispatch) { 2   return (...args) => dispatch(actionCreator(...args)) 3 } 4  5 /** 6  * Turns an object whose values are action creators, into an object with the 7  * same keys, but with every function wrapped into a `dispatch` call so they 8  * may be invoked directly. This is just a convenience method, as you can call 9  * `store.dispatch(MyActionCreators.doSomething())` yourself just fine.10  *11  * For convenience, you can also pass a single function as the first argument,12  * and get a function in return.13  *14  * @param {Function|Object} actionCreators An object whose values are action15  * creator functions. One handy way to obtain it is to use ES6 `import * as`16  * syntax. You may also pass a single function.17  *18  * @param {Function} dispatch The `dispatch` function available on your Redux19  * store.20  *21  * @returns {Function|Object} The object mimicking the original object, but with22  * every action creator wrapped into the `dispatch` call. If you passed a23  * function as `actionCreators`, the return value will also be a single24  * function.25  */26 export default function bindActionCreators(actionCreators, dispatch) {27   if (typeof actionCreators === 'function') {28     return bindActionCreator(actionCreators, dispatch)29   }30 31   if (typeof actionCreators !== 'object' || actionCreators === null) {32     throw new Error(33       `bindActionCreators expected an object or a function, instead received ${actionCreators === null ? 'null' : typeof actionCreators}. ` +34       `Did you write "import ActionCreators from" instead of "import * as ActionCreators from"?`35     )36   }37 38   var keys = Object.keys(actionCreators)39   var boundActionCreators = {}40   for (var i = 0; i < keys.length; i++) {41     var key = keys[i]42     var actionCreator = actionCreators[key]43     if (typeof actionCreator === 'function') {44       boundActionCreators[key] = bindActionCreator(actionCreator, dispatch)45     }46   }47   return boundActionCreators48 }

这个东西唯一的好处是往下传actionCreator和dispatch的时候,少些代码,无形中在props里加了内容!!!!

具体应用场景,大家自行谷歌。因为我也才学这个第8天,这函数我自己都没用过····哈哈。不过以后肯定要用了!!!

代码十分简单,我就不分析了。

这里有一篇园内的朋友写的它的应用,凑合看吧:

 

 

 

转载于:https://www.cnblogs.com/huenchao/p/6092266.html

转载地址:https://blog.csdn.net/weixin_34061042/article/details/93115803 如侵犯您的版权,请留言回复原文章的地址,我们会给您删除此文章,给您带来不便请您谅解!

上一篇:Linux学习之修改网络属性命令的基本使用
下一篇:ssh配置文件的参数

发表评论

最新留言

初次前来,多多关照!
[***.217.46.12]2024年04月22日 21时12分59秒