摘要:大家好,今天給大家?guī)淼氖堑脑创a分析首先是的地址點我接下來我們看看在項目中的簡單使用,一般我們都從最簡單的開始入手哈備注例子中結合的是進行使用,當然不僅僅能結合,還能結合市面上其他大多數的框架,這也是它比較流弊的地方首先是創(chuàng)建一個首先我們
大家好,今天給大家?guī)淼氖莚edux(v3.6.0)的源碼分析~
首先是redux的github地址 點我
接下來我們看看redux在項目中的簡單使用,一般我們都從最簡單的開始入手哈
備注:例子中結合的是react進行使用,當然redux不僅僅能結合react,還能結合市面上其他大多數的框架,這也是它比較流弊的地方
首先是創(chuàng)建一個store
import React from "react" import { render } from "react-dom" // 首先我們必須先導入redux中的createStore方法,用于創(chuàng)建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" // 創(chuàng)建存放中間件數組 const middleware = [ thunk ] if (process.env.NODE_ENV !== "production") { middleware.push(createLogger()) } // 調用createStore方法來創(chuàng)建store,傳入的參數分別是reducer和運用中間件的函數 const store = createStore( reducer, applyMiddleware(...middleware) ) // 將store作為屬性傳入,這樣在每個子組件中就都可以獲取這個store實例,然后使用store的方法 render(, document.getElementById("root") )
接下來我們看看reducer是怎么定義的
// 首先我們導入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源碼里面具體是怎么實現的吧
首先我們看看整個redux項目的目錄結構,從目錄中我們可以看出,redux的項目源碼其實比較簡單
接下來就從入口文件index.js開始看吧,這個文件其實沒有實現什么實質性的功能,只是導出了redux所提供的能力
// 入口文件 // 首先引入相應的模塊,具體模塊的內容后續(xù)會詳細分析 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。這個文件用于創(chuàng)建store
// 創(chuàng)建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. */ // 導出創(chuàng)建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函數,并且執(zhí)行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 // 定義當前的監(jiān)聽者隊列 let currentListeners = [] // 定義下一個循環(huán)的監(jiān)聽者隊列 let nextListeners = currentListeners // 定義一個判斷是否在dispatch的標志位 let isDispatching = false // 判斷是否能執(zhí)行下一次監(jiān)聽隊列 function ensureCanMutateNextListeners() { if (nextListeners === currentListeners) { // 這里是將當前監(jiān)聽隊列通過拷貝的形式賦值給下次監(jiān)聽隊列,這樣做是為了防止在當前隊列執(zhí)行的時候會影響到自身,所以拷貝了一份副本 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. */ // 往監(jiān)聽隊列里面去添加監(jiān)聽者 function subscribe(listener) { // 監(jiān)聽者必須是一個函數 if (typeof listener !== "function") { throw new Error("Expected listener to be a function.") } // 聲明一個變量來標記是否已經subscribed,通過閉包的形式被緩存 let isSubscribed = true // 創(chuàng)建一個當前currentListeners的副本,賦值給nextListeners ensureCanMutateNextListeners() // 將監(jiān)聽者函數push到nextListeners中 nextListeners.push(listener) // 返回一個取消監(jiān)聽的函數 // 原理很簡單就是從將當前函數從數組中刪除,使用的是數組的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,來觸發(fā)對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還沒結束,則不能繼續(xù)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結束之后,就執(zhí)行監(jiān)聽隊列中的監(jiān)聽函數 // 將nextListeners賦值給currentListeners,保證下一次執(zhí)行ensureCanMutateNextListeners方法的時候會重新拷貝一個新的副本 // 簡單粗暴的使用for循環(huán)執(zhí)行 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] // 執(zhí)行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是否發(fā)生改變 hasChanged = hasChanged || nextStateForKey !== previousStateForKey } // 如果state發(fā)生改變則返回新的state,否則返回原來的state return hasChanged ? nextState : state } }
接下來我們在看看bindActionCreators.js這個文件
首先先認識actionCreators,簡單來說就是創(chuàng)建action的方法,redux的action是一個對象,而我們經常使用一些函數來創(chuàng)建這些對象,則這些函數就是actionCreators
而這個文件實現的功能,是根據綁定的actionCreator,來實現自動dispatch的功能
import warning from "./utils/warning" // 對于每個actionCreator方法,執(zhí)行之后都會得到一個action // 這個bindActionCreator方法,會返回一個能夠自動執(zhí)行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的對應的值,也就是各個匿名函數,執(zhí)行匿名函數就可以實現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) { // 首先先返回一個匿名函數,有沒有發(fā)現這個函數跟createStore很相似啊 // 沒錯其實他就是我們的之前看到的createStore return (createStore) => (reducer, preloadedState, enhancer) => { // 首先用原來的createStore創(chuàng)建一個store,并把它緩存起來 const store = createStore(reducer, preloadedState, enhancer) // 獲取store中原始的dispatch方法 let dispatch = store.dispatch // 定一個執(zhí)行鏈數組 let chain = [] // 緩存原有store中getState和dispatch方法 const middlewareAPI = { getState: store.getState, dispatch: (action) => dispatch(action) } // 執(zhí)行每個中間件函數,并將middlewareAPI作為參數傳入,獲得一個執(zhí)行鏈數組 chain = middlewares.map(middleware => middleware(middlewareAPI)) // 將執(zhí)行鏈數組傳入compose方法中,并立即執(zhí)行返回的方法獲得最后包裝過后的dispatch // 這個過程簡單來說就是,每個中間件都會接受一個store.dispatch方法,然后基于這個方法進行包裝,然后返回一個新的dispatch // 這個新的dispatch又作為參數傳入下一個中間件函數,然后有進行包裝。。。一直循環(huán)這個過程,直到最后得到一個最終的dispatch dispatch = compose(...chain)(store.dispatch) // 返回一個store對象,并將新的dispatch方法覆蓋原有的dispatch方法 return { ...store, dispatch } } }
看到這里,其實你已經看完了大部分redux的內容,最后我們看看上述文件中使用到的compose方法是如何實現的。
打開compose.js,我們發(fā)現其實實現方式就是利用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 } // 如果函數數組只有一個元素,則直接執(zhí)行 if (funcs.length === 1) { return funcs[0] } // 否則,就利用reduce方法執(zhí)行每個中間件函數,并將上一個函數的返回作為下一個函數的參數 return funcs.reduce((a, b) => (...args) => a(b(...args))) }
哈哈,以上就是今天給大家分享的redux源碼分析~希望大家能夠喜歡咯
文章版權歸作者所有,未經允許請勿轉載,若此文章存在違規(guī)行為,您可以聯系管理員刪除。
轉載請注明本文地址:http://specialneedsforspecialkids.com/yun/84229.html
摘要:訂閱器不應該關注所有的變化,在訂閱器被調用之前,往往由于嵌套的導致發(fā)生多次的改變,我們應該保證所有的監(jiān)聽都注冊在之前。 前言 用 React + Redux 已經一段時間了,記得剛開始用Redux 的時候感覺非常繞,總搞不起里面的關系,如果大家用一段時間Redux又看了它的源碼話,對你的理解會有很大的幫助。看完后,在回來看Redux,有一種 柳暗花明又一村 的感覺 ,. 源碼 我分析的...
摘要:調用鏈中最后一個會接受真實的的方法作為參數,并借此結束調用鏈。總結我們常用的一般是除了和之外的方法,那個理解明白了,對于以后出現的問題會有很大幫助,本文只是針對最基礎的進行解析,之后有機會繼續(xù)解析對他的封裝 前言 雖然一直使用redux+react-redux,但是并沒有真正去講redux最基礎的部分理解透徹,我覺得理解明白redux會對react-redux有一個透徹的理解。 其實,...
摘要:函數的柯里化的基本使用方法和函數綁定是一樣的使用一個閉包返回一個函數。先來一段我自己實現的函數高程里面這么評價它們兩個的方法也實現了函數的柯里化。使用還是要根據是否需要對象響應來決定。 奇怪,怎么把函數的柯里化和Redux中間件這兩個八竿子打不著的東西聯系到了一起,如果你和我有同樣疑問的話,說明你對Redux中間件的原理根本就不了解,我們先來講下什么是函數的柯里化?再來講下Redux的...
摘要:另外,內置的函數在經過一系列校驗后,觸發(fā),之后被更改,之后依次調用監(jiān)聽,完成整個狀態(tài)樹的更新。總而言之,遵守這套規(guī)范并不是強制性的,但是項目一旦稍微復雜一些,這樣做的好處就可以充分彰顯出來。 這一篇是接上一篇react進階漫談的第二篇,這一篇主要分析redux的思想和應用,同樣參考了網絡上的大量資料,但代碼同樣都是自己嘗試實踐所得,在這里分享出來,僅供一起學習(上一篇地址:個人博客/s...
閱讀 1575·2021-11-23 10:01
閱讀 2969·2021-11-19 09:40
閱讀 3214·2021-10-18 13:24
閱讀 3464·2019-08-29 14:20
閱讀 2980·2019-08-26 13:39
閱讀 1276·2019-08-26 11:56
閱讀 2662·2019-08-23 18:03
閱讀 373·2019-08-23 15:35