你們好,今天給你們帶來的是redux(v3.6.0)的源碼分析~react
首先是redux的github地址 點我webpack
接下來咱們看看redux在項目中的簡單使用,通常咱們都從最簡單的開始入手哈git
備註:例子中結合的是react進行使用,固然redux不只僅能結合react,還能結合市面上其餘大多數的框架,這也是它比較流弊的地方github
首先是建立一個storeweb
import React from 'react' import { render } from 'react-dom' // 首先咱們必須先導入redux中的createStore方法,用於建立store // 導入applyMiddleware方法,用於使用中間件 import { createStore, applyMiddleware } from 'redux' import { Provider } from 'react-redux' // 導入redux的中間件thunk import thunk from 'redux-thunk' // 導入redux的中間件createLogger import { createLogger } from 'redux-logger' // 咱們還必須本身定義reducer函數,用於根據咱們傳入的action來訪問新的state import reducer from './reducers' import App from './containers/App' // 建立存放中間件數組 const middleware = [ thunk ] if (process.env.NODE_ENV !== 'production') { middleware.push(createLogger()) } // 調用createStore方法來建立store,傳入的參數分別是reducer和運用中間件的函數 const store = createStore( reducer, applyMiddleware(...middleware) ) // 將store做爲屬性傳入,這樣在每一個子組件中就均可以獲取這個store實例,而後使用store的方法 render( <Provider store={store}> <App /> </Provider>, document.getElementById('root') )
接下來咱們看看reducer是怎麼定義的express
// 首先咱們導入redux中的combineReducers方法 import { combineReducers } from 'redux' // 導入actions,這個非必須,可是推薦這麼作 import { SELECT_REDDIT, INVALIDATE_REDDIT, REQUEST_POSTS, RECEIVE_POSTS } from '../actions' // 接下來這個兩個方法selectedReddit,postsByReddit就是reducer方法 // reducer方法負責根據傳入的action的類型,返回新的state,這裏能夠傳入默認的state const selectedReddit = (state = 'reactjs', action) => { switch (action.type) { case SELECT_REDDIT: return action.reddit default: return state } } const posts = (state = { isFetching: false, didInvalidate: false, items: [] }, action) => { switch (action.type) { case INVALIDATE_REDDIT: return { ...state, didInvalidate: true } case REQUEST_POSTS: return { ...state, isFetching: true, didInvalidate: false } case RECEIVE_POSTS: return { ...state, isFetching: false, didInvalidate: false, items: action.posts, lastUpdated: action.receivedAt } default: return state } } const postsByReddit = (state = { }, action) => { switch (action.type) { case INVALIDATE_REDDIT: case RECEIVE_POSTS: case REQUEST_POSTS: return { ...state, [action.reddit]: posts(state[action.reddit], action) } default: return state } } // 最後咱們經過combineReducers這個方法,將全部的reducer方法合併成一個方法,也就是rootReducer方法 const rootReducer = combineReducers({ postsByReddit, selectedReddit }) // 導出這個rootReducer方法 export default rootReducer
接下來看看action的定義,其實action就是一個對象,對象中約定有一個必要的屬性type,和一個非必要的屬性payload;type表明了action的類型,指明瞭這個action對state修改的意圖,而payload則是傳入一些額外的數據供reducer使用編程
export const REQUEST_POSTS = 'REQUEST_POSTS' export const RECEIVE_POSTS = 'RECEIVE_POSTS' export const SELECT_REDDIT = 'SELECT_REDDIT' export const INVALIDATE_REDDIT = 'INVALIDATE_REDDIT' export const selectReddit = reddit => ({ type: SELECT_REDDIT, reddit }) export const invalidateReddit = reddit => ({ type: INVALIDATE_REDDIT, reddit }) export const requestPosts = reddit => ({ type: REQUEST_POSTS, reddit }) export const receivePosts = (reddit, json) => ({ type: RECEIVE_POSTS, reddit, posts: json.data.children.map(child => child.data), receivedAt: Date.now() }) const fetchPosts = reddit => dispatch => { dispatch(requestPosts(reddit)) return fetch(`https://www.reddit.com/r/${reddit}.json`) .then(response => response.json()) .then(json => dispatch(receivePosts(reddit, json))) } const shouldFetchPosts = (state, reddit) => { const posts = state.postsByReddit[reddit] if (!posts) { return true } if (posts.isFetching) { return false } return posts.didInvalidate } export const fetchPostsIfNeeded = reddit => (dispatch, getState) => { if (shouldFetchPosts(getState(), reddit)) { return dispatch(fetchPosts(reddit)) } }
以上就是redux最簡單的用法,接下來咱們就來看看redux源碼裏面具體是怎麼實現的吧json
首先咱們看看整個redux項目的目錄結構,從目錄中咱們能夠看出,redux的項目源碼其實比較簡單redux
接下來就從入口文件index.js開始看吧,這個文件其實沒有實現什麼實質性的功能,只是導出了redux所提供的能力數組
// 入口文件 // 首先引入相應的模塊,具體模塊的內容後續會詳細分析 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' /* * 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 DefinePlugin for webpack (http://stackoverflow.com/questions/30030031) ' + 'to ensure you have the correct code for your production build.' ) } // 導出相應的功能 export { createStore, combineReducers, bindActionCreators, applyMiddleware, compose }
緊接着,咱們就來看看redux中一個重要的文件,createStore.js。這個文件用於建立store
// 建立store的文件,提供了redux中store的全部內置的功能,也是redux中比較重要的一個文件 // 首先引入相應的模塊 import isPlainObject from 'lodash/isPlainObject' import $$observable from 'symbol-observable' /** * These are private action types reserved by Redux. * For any unknown actions, you must return the current state. * If the current state is undefined, you must return the initial state. * Do not reference these action types directly in your code. */ // 定義了有個內部使用的ActionType export const ActionTypes = { INIT: '@@redux/INIT' } /** * Creates a Redux store that holds the state tree. * The only way to change the data in the store is to call `dispatch()` on it. * * There should only be a single store in your app. To specify how different * parts of the state tree respond to actions, you may combine several reducers * into a single reducer function by using `combineReducers`. * * @param {Function} reducer A function that returns the next state tree, given * the current state tree and the action to handle. * * @param {any} [preloadedState] The initial state. You may optionally specify it * to hydrate the state from the server in universal apps, or to restore a * previously serialized user session. * If you use `combineReducers` to produce the root reducer function, this must be * an object with the same shape as `combineReducers` keys. * * @param {Function} [enhancer] The store enhancer. You may optionally specify it * to enhance the store with third-party capabilities such as middleware, * time travel, persistence, etc. The only store enhancer that ships with Redux * is `applyMiddleware()`. * * @returns {Store} A Redux store that lets you read the state, dispatch actions * and subscribe to changes. */ // 導出建立store的方法 // 這個方法接收三個參數,分別是 reducer,預先加載的state,以及功能加強函數enhancer export default function createStore(reducer, preloadedState, enhancer) { // 調整參數,若是沒有傳入預先加載的state,而且第二個參數是一個函數的話,則把第二個參數爲功能加強函數enhancer if (typeof preloadedState === 'function' && typeof enhancer === 'undefined') { enhancer = preloadedState preloadedState = undefined } // 判斷enhancer必須是一個函數 if (typeof enhancer !== 'undefined') { if (typeof enhancer !== 'function') { throw new Error('Expected the enhancer to be a function.') } // 這是一個很重要的處理,它將createStore方法做爲參數傳入enhancer函數,而且執行enhancer // 這裏主要是提供給redux中間件的使用,以此來達到加強整個redux流程的效果 // 經過這個函數,也給redux提供了無限多的可能性 return enhancer(createStore)(reducer, preloadedState) } // reducer必須是一個函數,不然報錯 if (typeof reducer !== 'function') { throw new Error('Expected the reducer to be a function.') } // 將傳入的reducer緩存到currentReducer變量中 let currentReducer = reducer // 將傳入的preloadedState緩存到currentState變量中 let currentState = preloadedState // 定義當前的監聽者隊列 let currentListeners = [] // 定義下一個循環的監聽者隊列 let nextListeners = currentListeners // 定義一個判斷是否在dispatch的標誌位 let isDispatching = false // 判斷是否能執行下一次監聽隊列 function ensureCanMutateNextListeners() { if (nextListeners === currentListeners) { // 這裏是將當前監聽隊列經過拷貝的形式賦值給下次監聽隊列,這樣作是爲了防止在當前隊列執行的時候會影響到自身,因此拷貝了一份副本 nextListeners = currentListeners.slice() } } /** * Reads the state tree managed by the store. * * @returns {any} The current state tree of your application. */ // 獲取當前的state function getState() { return currentState } /** * Adds a change listener. It will be called any time an action is dispatched, * and some part of the state tree may potentially have changed. You may then * call `getState()` to read the current state tree inside the callback. * * You may call `dispatch()` from a change listener, with the following * caveats: * * 1. The subscriptions are snapshotted just before every `dispatch()` call. * If you subscribe or unsubscribe while the listeners are being invoked, this * will not have any effect on the `dispatch()` that is currently in progress. * However, the next `dispatch()` call, whether nested or not, will use a more * recent snapshot of the subscription list. * * 2. The listener should not expect to see all state changes, as the state * might have been updated multiple times during a nested `dispatch()` before * the listener is called. It is, however, guaranteed that all subscribers * registered before the `dispatch()` started will be called with the latest * state by the time it exits. * * @param {Function} listener A callback to be invoked on every dispatch. * @returns {Function} A function to remove this change listener. */ // 往監聽隊列裏面去添加監聽者 function subscribe(listener) { // 監聽者必須是一個函數 if (typeof listener !== 'function') { throw new Error('Expected listener to be a function.') } // 聲明一個變量來標記是否已經subscribed,經過閉包的形式被緩存 let isSubscribed = true // 建立一個當前currentListeners的副本,賦值給nextListeners ensureCanMutateNextListeners() // 將監聽者函數push到nextListeners中 nextListeners.push(listener) // 返回一個取消監聽的函數 // 原理很簡單就是從將當前函數從數組中刪除,使用的是數組的splice方法 return function unsubscribe() { if (!isSubscribed) { return } isSubscribed = false ensureCanMutateNextListeners() const index = nextListeners.indexOf(listener) nextListeners.splice(index, 1) } } /** * Dispatches an action. It is the only way to trigger a state change. * * The `reducer` function, used to create the store, will be called with the * current state tree and the given `action`. Its return value will * be considered the **next** state of the tree, and the change listeners * will be notified. * * The base implementation only supports plain object actions. If you want to * dispatch a Promise, an Observable, a thunk, or something else, you need to * wrap your store creating function into the corresponding middleware. For * example, see the documentation for the `redux-thunk` package. Even the * middleware will eventually dispatch plain object actions using this method. * * @param {Object} action A plain object representing 「what changed」. It is * a good idea to keep actions serializable so you can record and replay user * sessions, or use the time travelling `redux-devtools`. An action must have * a `type` property which may not be `undefined`. It is a good idea to use * string constants for action types. * * @returns {Object} For convenience, the same action object you dispatched. * * Note that, if you use a custom middleware, it may wrap `dispatch()` to * return something else (for example, a Promise you can await). */ // redux中經過dispatch一個action,來觸發對store中的state的修改 // 參數就是一個action function dispatch(action) { // 這裏判斷一下action是不是一個純對象,若是不是則拋出錯誤 if (!isPlainObject(action)) { throw new Error( 'Actions must be plain objects. ' + 'Use custom middleware for async actions.' ) } // action中必需要有type屬性,不然拋出錯誤 if (typeof action.type === 'undefined') { throw new Error( 'Actions may not have an undefined "type" property. ' + 'Have you misspelled a constant?' ) } // 若是上一次dispatch還沒結束,則不能繼續dispatch下一次 if (isDispatching) { throw new Error('Reducers may not dispatch actions.') } try { // 將isDispatching設置爲true,表示當次dispatch開始 isDispatching = true // 利用傳入的reducer函數處理state和action,返回新的state // 推薦不直接修改原有的currentState currentState = currentReducer(currentState, action) } finally { // 當次的dispatch結束 isDispatching = false } // 每次dispatch結束以後,就執行監聽隊列中的監聽函數 // 將nextListeners賦值給currentListeners,保證下一次執行ensureCanMutateNextListeners方法的時候會從新拷貝一個新的副本 // 簡單粗暴的使用for循環執行 const listeners = currentListeners = nextListeners for (let i = 0; i < listeners.length; i++) { const listener = listeners[i] listener() } // 最後返回action return action } /** * Replaces the reducer currently used by the store to calculate the state. * * You might need this if your app implements code splitting and you want to * load some of the reducers dynamically. You might also need this if you * implement a hot reloading mechanism for Redux. * * @param {Function} nextReducer The reducer for the store to use instead. * @returns {void} */ // replaceReducer方法,顧名思義就是替換當前的reducer處理函數 function replaceReducer(nextReducer) { if (typeof nextReducer !== 'function') { throw new Error('Expected the nextReducer to be a function.') } currentReducer = nextReducer dispatch({ type: ActionTypes.INIT }) } /** * Interoperability point for observable/reactive libraries. * @returns {observable} A minimal observable of state changes. * For more information, see the observable proposal: * https://github.com/tc39/proposal-observable */ // 這個函數通常來講用不到,他是配合其餘特色的框架或編程思想來使用的如rx.js,感興趣的朋友能夠自行學習 // 這裏就很少作介紹 function observable() { const outerSubscribe = subscribe return { /** * The minimal observable subscription method. * @param {Object} observer Any object that can be used as an observer. * The observer object should have a `next` method. * @returns {subscription} An object with an `unsubscribe` method that can * be used to unsubscribe the observable from the store, and prevent further * emission of values from the observable. */ subscribe(observer) { if (typeof observer !== 'object') { throw new TypeError('Expected the observer to be an object.') } function observeState() { if (observer.next) { observer.next(getState()) } } observeState() const unsubscribe = outerSubscribe(observeState) return { unsubscribe } }, [$$observable]() { return this } } } // When a store is created, an "INIT" action is dispatched so that every // reducer returns their initial state. This effectively populates // the initial state tree. // dispatch一個初始化的action dispatch({ type: ActionTypes.INIT }) // 最後返回這個store的全部能力 return { dispatch, subscribe, getState, replaceReducer, [$$observable]: observable } }
接下來咱們看看combineReducers.js這個文件,一般咱們會用它來合併咱們的reducer方法
這個文件用於合併多個reducer,而後返回一個根reducer
由於store中只容許有一個reducer函數,因此當咱們須要進行模塊拆分的時候,就必需要用到這個方法
// 一開始先導入相應的函數 import { ActionTypes } from './createStore' import isPlainObject from 'lodash/isPlainObject' import warning from './utils/warning' // 獲取UndefinedState的錯誤信息 function getUndefinedStateErrorMessage(key, action) { const actionType = action && action.type const actionName = (actionType && `"${actionType.toString()}"`) || 'an action' return ( `Given action ${actionName}, reducer "${key}" returned undefined. ` + `To ignore an action, you must explicitly return the previous state. ` + `If you want this reducer to hold no value, you can return null instead of undefined.` ) } function getUnexpectedStateShapeWarningMessage(inputState, reducers, action, unexpectedKeyCache) { // 獲取reducers的全部key const reducerKeys = Object.keys(reducers) const argumentName = action && action.type === ActionTypes.INIT ? 'preloadedState argument passed to createStore' : 'previous state received by the reducer' // 當reducers對象是一個空對象的話,返回警告文案 if (reducerKeys.length === 0) { return ( 'Store does not have a valid reducer. Make sure the argument passed ' + 'to combineReducers is an object whose values are reducers.' ) } // state必須是一個對象 if (!isPlainObject(inputState)) { return ( `The ${argumentName} has unexpected type of "` + ({}).toString.call(inputState).match(/\s([a-z|A-Z]+)/)[1] + `". Expected argument to be an object with the following ` + `keys: "${reducerKeys.join('", "')}"` ) } // 判斷state中是否有reducer沒有的key,由於redux對state分模塊的時候,是依據reducer來劃分的 const unexpectedKeys = Object.keys(inputState).filter(key => !reducers.hasOwnProperty(key) && !unexpectedKeyCache[key] ) unexpectedKeys.forEach(key => { unexpectedKeyCache[key] = true }) if (unexpectedKeys.length > 0) { return ( `Unexpected ${unexpectedKeys.length > 1 ? 'keys' : 'key'} ` + `"${unexpectedKeys.join('", "')}" found in ${argumentName}. ` + `Expected to find one of the known reducer keys instead: ` + `"${reducerKeys.join('", "')}". Unexpected keys will be ignored.` ) } } // assertReducerShape函數,檢測當遇到位置action的時候,reducer是否會返回一個undefined,若是是的話則拋出錯誤 // 接受一個reducers對象 function assertReducerShape(reducers) { // 遍歷這個reducers對象 Object.keys(reducers).forEach(key => { const reducer = reducers[key] // 獲取reducer函數在處理當state是undefined,actionType爲初始默認type的時候返回的值 const initialState = reducer(undefined, { type: ActionTypes.INIT }) // 若是這個值是undefined,則拋出錯誤,由於初始state不該該是undefined if (typeof initialState === 'undefined') { throw new Error( `Reducer "${key}" returned undefined during initialization. ` + `If the state passed to the reducer is undefined, you must ` + `explicitly return the initial state. The initial state may ` + `not be undefined. If you don't want to set a value for this reducer, ` + `you can use null instead of undefined.` ) } // 當遇到一個不知道的action的時候,reducer也不能返回undefined,不然也會拋出報錯 const type = '@@redux/PROBE_UNKNOWN_ACTION_' + Math.random().toString(36).substring(7).split('').join('.') if (typeof reducer(undefined, { type }) === 'undefined') { throw new Error( `Reducer "${key}" returned undefined when probed with a random type. ` + `Don't try to handle ${ActionTypes.INIT} or other actions in "redux/*" ` + `namespace. They are considered private. Instead, you must return the ` + `current state for any unknown actions, unless it is undefined, ` + `in which case you must return the initial state, regardless of the ` + `action type. The initial state may not be undefined, but can be null.` ) } }) } /** * Turns an object whose values are different reducer functions, into a single * reducer function. It will call every child reducer, and gather their results * into a single state object, whose keys correspond to the keys of the passed * reducer functions. * * @param {Object} reducers An object whose values correspond to different * reducer functions that need to be combined into one. One handy way to obtain * it is to use ES6 `import * as reducers` syntax. The reducers may never return * undefined for any action. Instead, they should return their initial state * if the state passed to them was undefined, and the current state for any * unrecognized action. * * @returns {Function} A reducer function that invokes every reducer inside the * passed object, and builds a state object with the same shape. */ // 導出combineReducers方法,接受一個參數reducers對象 export default function combineReducers(reducers) { // 獲取reducers對象的key值 const reducerKeys = Object.keys(reducers) // 定義一個最終要返回的reducers對象 const finalReducers = {} // 遍歷這個reducers對象的key for (let i = 0; i < reducerKeys.length; i++) { // 緩存每一個key值 const key = reducerKeys[i] if (process.env.NODE_ENV !== 'production') { if (typeof reducers[key] === 'undefined') { warning(`No reducer provided for key "${key}"`) } } // 相應key的值是個函數,則將改函數緩存到finalReducers中 if (typeof reducers[key] === 'function') { finalReducers[key] = reducers[key] } } // 獲取finalReducers的全部的key值,緩存到變量finalReducerKeys中 const finalReducerKeys = Object.keys(finalReducers) let unexpectedKeyCache if (process.env.NODE_ENV !== 'production') { unexpectedKeyCache = {} } // 定義一個變量,用於緩存錯誤對象 let shapeAssertionError try { // 作錯誤處理,詳情看後面assertReducerShape方法 // 主要就是檢測, assertReducerShape(finalReducers) } catch (e) { shapeAssertionError = e } return function combination(state = {}, action) { // 若是有錯誤,則拋出錯誤 if (shapeAssertionError) { throw shapeAssertionError } if (process.env.NODE_ENV !== 'production') { // 獲取警告提示 const warningMessage = getUnexpectedStateShapeWarningMessage(state, finalReducers, action, unexpectedKeyCache) if (warningMessage) { warning(warningMessage) } } // 定義一個變量來表示state是否已經被改變 let hasChanged = false // 定義一個變量,來緩存改變後的state const nextState = {} // 開始遍歷finalReducerKeys for (let i = 0; i < finalReducerKeys.length; i++) { // 獲取有效的reducer的key值 const key = finalReducerKeys[i] // 根據key值獲取對應的reducer函數 const reducer = finalReducers[key] // 根據key值獲取對應的state模塊 const previousStateForKey = state[key] // 執行reducer函數,獲取相應模塊的state const nextStateForKey = reducer(previousStateForKey, action) // 若是獲取的state是undefined,則拋出錯誤 if (typeof nextStateForKey === 'undefined') { const errorMessage = getUndefinedStateErrorMessage(key, action) throw new Error(errorMessage) } // 將獲取到的新的state賦值給新的state對應的模塊,key則爲當前reducer的key nextState[key] = nextStateForKey // 判讀state是否發生改變 hasChanged = hasChanged || nextStateForKey !== previousStateForKey } // 若是state發生改變則返回新的state,不然返回原來的state return hasChanged ? nextState : state } }
接下來咱們在看看bindActionCreators.js這個文件
首先先認識actionCreators,簡單來講就是建立action的方法,redux的action是一個對象,而咱們常用一些函數來建立這些對象,則這些函數就是actionCreators
而這個文件實現的功能,是根據綁定的actionCreator,來實現自動dispatch的功能
import warning from './utils/warning' // 對於每一個actionCreator方法,執行以後都會獲得一個action // 這個bindActionCreator方法,會返回一個可以自動執行dispatch的方法 function bindActionCreator(actionCreator, dispatch) { return (...args) => dispatch(actionCreator(...args)) } /** * Turns an object whose values are action creators, into an object with the * same keys, but with every function wrapped into a `dispatch` call so they * may be invoked directly. This is just a convenience method, as you can call * `store.dispatch(MyActionCreators.doSomething())` yourself just fine. * * For convenience, you can also pass a single function as the first argument, * and get a function in return. * * @param {Function|Object} actionCreators An object whose values are action * creator functions. One handy way to obtain it is to use ES6 `import * as` * syntax. You may also pass a single function. * * @param {Function} dispatch The `dispatch` function available on your Redux * store. * * @returns {Function|Object} The object mimicking the original object, but with * every action creator wrapped into the `dispatch` call. If you passed a * function as `actionCreators`, the return value will also be a single * function. */ // 對外暴露這個bindActionCreators方法 export default function bindActionCreators(actionCreators, dispatch) { // 若是傳入的actionCreators參數是個函數,則直接調用bindActionCreator方法 if (typeof actionCreators === 'function') { return bindActionCreator(actionCreators, dispatch) } // 錯誤處理 if (typeof actionCreators !== 'object' || actionCreators === null) { throw new Error( `bindActionCreators expected an object or a function, instead received ${actionCreators === null ? 'null' : typeof actionCreators}. ` + `Did you write "import ActionCreators from" instead of "import * as ActionCreators from"?` ) } // 若是actionCreators是一個對象,則獲取對象中的key const keys = Object.keys(actionCreators) // 定義一個緩存對象 const boundActionCreators = {} // 遍歷actionCreators的每一個key for (let i = 0; i < keys.length; i++) { // 獲取每一個key const key = keys[i] // 根據每一個key獲取特定的actionCreator方法 const actionCreator = actionCreators[key] // 若是actionCreator是一個函數,則直接調用bindActionCreator方法,將返回的匿名函數緩存到boundActionCreators對象中 if (typeof actionCreator === 'function') { boundActionCreators[key] = bindActionCreator(actionCreator, dispatch) } else { warning(`bindActionCreators expected a function actionCreator for key '${key}', instead received type '${typeof actionCreator}'.`) } } // 最後返回boundActionCreators對象 // 用戶獲取到這個對象後,可拿出對象中的每一個key的對應的值,也就是各個匿名函數,執行匿名函數就能夠實現dispatch功能 return boundActionCreators }
接下來咱們看看applyMiddleware.js這個文件,這個文件讓redux有着無限多的可能性。爲何這麼說呢,你往下看就知道了
// 這個文件的代碼邏輯其實很簡單 // 首先導入compose函數,等一下咱們會詳細分析這個compose函數 import compose from './compose' /** * 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這個方法,這個方法也是咱們常常用來做爲createStore中enhance參數的一個方法 export default function applyMiddleware(...middlewares) { // 首先先返回一個匿名函數,有沒有發現這個函數跟createStore很類似啊 // 沒錯其實他就是咱們的以前看到的createStore return (createStore) => (reducer, preloadedState, enhancer) => { // 首先用原來的createStore建立一個store,並把它緩存起來 const store = createStore(reducer, preloadedState, enhancer) // 獲取store中原始的dispatch方法 let dispatch = store.dispatch // 定一個執行鏈數組 let chain = [] // 緩存原有store中getState和dispatch方法 const middlewareAPI = { getState: store.getState, dispatch: (action) => dispatch(action) } // 執行每一箇中間件函數,並將middlewareAPI做爲參數傳入,得到一個執行鏈數組 chain = middlewares.map(middleware => middleware(middlewareAPI)) // 將執行鏈數組傳入compose方法中,並當即執行返回的方法得到最後包裝事後的dispatch // 這個過程簡單來講就是,每一箇中間件都會接受一個store.dispatch方法,而後基於這個方法進行包裝,而後返回一個新的dispatch // 這個新的dispatch又做爲參數傳入下一個中間件函數,而後有進行包裝。。。一直循環這個過程,直到最後獲得一個最終的dispatch dispatch = compose(...chain)(store.dispatch) // 返回一個store對象,並將新的dispatch方法覆蓋原有的dispatch方法 return { ...store, dispatch } } }
看到這裏,其實你已經看完了大部分redux的內容,最後咱們看看上述文件中使用到的compose方法是如何實現的。
打開compose.js,咱們發現其實實現方式就是利用es5中數組的reduce方法來實現這種效果的
/** * Composes single-argument functions from right to left. The rightmost * function can take multiple arguments as it provides the signature for * the resulting composite function. * * @param {...Function} funcs The functions to compose. * @returns {Function} A function obtained by composing the argument functions * from right to left. For example, compose(f, g, h) is identical to doing * (...args) => f(g(h(...args))). */ export default function compose(...funcs) { // 判斷函數數組是否爲空 if (funcs.length === 0) { return arg => arg } // 若是函數數組只有一個元素,則直接執行 if (funcs.length === 1) { return funcs[0] } // 不然,就利用reduce方法執行每一箇中間件函數,並將上一個函數的返回做爲下一個函數的參數 return funcs.reduce((a, b) => (...args) => a(b(...args))) }
哈哈,以上就是今天給你們分享的redux源碼分析~但願你們可以喜歡咯