摘要:大家好,今天給大家?guī)?lái)的是的源碼分析首先是的地址點(diǎn)我接下來(lái)我們看看在項(xiàng)目中的簡(jiǎn)單使用,一般我們都從最簡(jiǎn)單的開(kāi)始入手哈備注例子中結(jié)合的是進(jìn)行使用,當(dāng)然不僅僅能結(jié)合,還能結(jié)合市面上其他大多數(shù)的框架,這也是它比較流弊的地方首先是創(chuàng)建一個(gè)首先我們
大家好,今天給大家?guī)?lái)的是redux(v3.6.0)的源碼分析~
首先是redux的github地址 點(diǎn)我
接下來(lái)我們看看redux在項(xiàng)目中的簡(jiǎn)單使用,一般我們都從最簡(jiǎn)單的開(kāi)始入手哈
備注:例子中結(jié)合的是react進(jìn)行使用,當(dāng)然redux不僅僅能結(jié)合react,還能結(jié)合市面上其他大多數(shù)的框架,這也是它比較流弊的地方
首先是創(chuàng)建一個(gè)store
import React from "react" import { render } from "react-dom" // 首先我們必須先導(dǎo)入redux中的createStore方法,用于創(chuàng)建store // 導(dǎo)入applyMiddleware方法,用于使用中間件 import { createStore, applyMiddleware } from "redux" import { Provider } from "react-redux" // 導(dǎo)入redux的中間件thunk import thunk from "redux-thunk" // 導(dǎo)入redux的中間件createLogger import { createLogger } from "redux-logger" // 我們還必須自己定義reducer函數(shù),用于根據(jù)我們傳入的action來(lái)訪問(wèn)新的state import reducer from "./reducers" import App from "./containers/App" // 創(chuàng)建存放中間件數(shù)組 const middleware = [ thunk ] if (process.env.NODE_ENV !== "production") { middleware.push(createLogger()) } // 調(diào)用createStore方法來(lái)創(chuàng)建store,傳入的參數(shù)分別是reducer和運(yùn)用中間件的函數(shù) const store = createStore( reducer, applyMiddleware(...middleware) ) // 將store作為屬性傳入,這樣在每個(gè)子組件中就都可以獲取這個(gè)store實(shí)例,然后使用store的方法 render(, document.getElementById("root") )
接下來(lái)我們看看reducer是怎么定義的
// 首先我們導(dǎo)入redux中的combineReducers方法 import { combineReducers } from "redux" // 導(dǎo)入actions,這個(gè)非必須,但是推薦這么做 import { SELECT_REDDIT, INVALIDATE_REDDIT, REQUEST_POSTS, RECEIVE_POSTS } from "../actions" // 接下來(lái)這個(gè)兩個(gè)方法selectedReddit,postsByReddit就是reducer方法 // reducer方法負(fù)責(zé)根據(jù)傳入的action的類型,返回新的state,這里可以傳入默認(rèn)的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 } } // 最后我們通過(guò)combineReducers這個(gè)方法,將所有的reducer方法合并成一個(gè)方法,也就是rootReducer方法 const rootReducer = combineReducers({ postsByReddit, selectedReddit }) // 導(dǎo)出這個(gè)rootReducer方法 export default rootReducer
接下來(lái)看看action的定義,其實(shí)action就是一個(gè)對(duì)象,對(duì)象中約定有一個(gè)必要的屬性type,和一個(gè)非必要的屬性payload;type代表了action的類型,指明了這個(gè)action對(duì)state修改的意圖,而payload則是傳入一些額外的數(shù)據(jù)供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最簡(jiǎn)單的用法,接下來(lái)我們就來(lái)看看redux源碼里面具體是怎么實(shí)現(xiàn)的吧
首先我們看看整個(gè)redux項(xiàng)目的目錄結(jié)構(gòu),從目錄中我們可以看出,redux的項(xiàng)目源碼其實(shí)比較簡(jiǎn)單
接下來(lái)就從入口文件index.js開(kāi)始看吧,這個(gè)文件其實(shí)沒(méi)有實(shí)現(xiàn)什么實(shí)質(zhì)性的功能,只是導(dǎo)出了redux所提供的能力
// 入口文件 // 首先引入相應(yīng)的模塊,具體模塊的內(nèi)容后續(xù)會(huì)詳細(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." ) } // 導(dǎo)出相應(yīng)的功能 export { createStore, combineReducers, bindActionCreators, applyMiddleware, compose }
緊接著,我們就來(lái)看看redux中一個(gè)重要的文件,createStore.js。這個(gè)文件用于創(chuàng)建store
// 創(chuàng)建store的文件,提供了redux中store的所有內(nèi)置的功能,也是redux中比較重要的一個(gè)文件 // 首先引入相應(yīng)的模塊 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. */ // 定義了有個(gè)內(nèi)部使用的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. */ // 導(dǎo)出創(chuàng)建store的方法 // 這個(gè)方法接收三個(gè)參數(shù),分別是 reducer,預(yù)先加載的state,以及功能增強(qiáng)函數(shù)enhancer export default function createStore(reducer, preloadedState, enhancer) { // 調(diào)整參數(shù),如果沒(méi)有傳入預(yù)先加載的state,并且第二個(gè)參數(shù)是一個(gè)函數(shù)的話,則把第二個(gè)參數(shù)為功能增強(qiáng)函數(shù)enhancer if (typeof preloadedState === "function" && typeof enhancer === "undefined") { enhancer = preloadedState preloadedState = undefined } // 判斷enhancer必須是一個(gè)函數(shù) if (typeof enhancer !== "undefined") { if (typeof enhancer !== "function") { throw new Error("Expected the enhancer to be a function.") } // 這是一個(gè)很重要的處理,它將createStore方法作為參數(shù)傳入enhancer函數(shù),并且執(zhí)行enhancer // 這里主要是提供給redux中間件的使用,以此來(lái)達(dá)到增強(qiáng)整個(gè)redux流程的效果 // 通過(guò)這個(gè)函數(shù),也給redux提供了無(wú)限多的可能性 return enhancer(createStore)(reducer, preloadedState) } // reducer必須是一個(gè)函數(shù),否則報(bào)錯(cuò) if (typeof reducer !== "function") { throw new Error("Expected the reducer to be a function.") } // 將傳入的reducer緩存到currentReducer變量中 let currentReducer = reducer // 將傳入的preloadedState緩存到currentState變量中 let currentState = preloadedState // 定義當(dāng)前的監(jiān)聽(tīng)者隊(duì)列 let currentListeners = [] // 定義下一個(gè)循環(huán)的監(jiān)聽(tīng)者隊(duì)列 let nextListeners = currentListeners // 定義一個(gè)判斷是否在dispatch的標(biāo)志位 let isDispatching = false // 判斷是否能執(zhí)行下一次監(jiān)聽(tīng)隊(duì)列 function ensureCanMutateNextListeners() { if (nextListeners === currentListeners) { // 這里是將當(dāng)前監(jiān)聽(tīng)隊(duì)列通過(guò)拷貝的形式賦值給下次監(jiān)聽(tīng)隊(duì)列,這樣做是為了防止在當(dāng)前隊(duì)列執(zhí)行的時(shí)候會(huì)影響到自身,所以拷貝了一份副本 nextListeners = currentListeners.slice() } } /** * Reads the state tree managed by the store. * * @returns {any} The current state tree of your application. */ // 獲取當(dāng)前的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)聽(tīng)隊(duì)列里面去添加監(jiān)聽(tīng)者 function subscribe(listener) { // 監(jiān)聽(tīng)者必須是一個(gè)函數(shù) if (typeof listener !== "function") { throw new Error("Expected listener to be a function.") } // 聲明一個(gè)變量來(lái)標(biāo)記是否已經(jīng)subscribed,通過(guò)閉包的形式被緩存 let isSubscribed = true // 創(chuàng)建一個(gè)當(dāng)前currentListeners的副本,賦值給nextListeners ensureCanMutateNextListeners() // 將監(jiān)聽(tīng)者函數(shù)push到nextListeners中 nextListeners.push(listener) // 返回一個(gè)取消監(jiān)聽(tīng)的函數(shù) // 原理很簡(jiǎn)單就是從將當(dāng)前函數(shù)從數(shù)組中刪除,使用的是數(shù)組的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中通過(guò)dispatch一個(gè)action,來(lái)觸發(fā)對(duì)store中的state的修改 // 參數(shù)就是一個(gè)action function dispatch(action) { // 這里判斷一下action是否是一個(gè)純對(duì)象,如果不是則拋出錯(cuò)誤 if (!isPlainObject(action)) { throw new Error( "Actions must be plain objects. " + "Use custom middleware for async actions." ) } // action中必須要有type屬性,否則拋出錯(cuò)誤 if (typeof action.type === "undefined") { throw new Error( "Actions may not have an undefined "type" property. " + "Have you misspelled a constant?" ) } // 如果上一次dispatch還沒(méi)結(jié)束,則不能繼續(xù)dispatch下一次 if (isDispatching) { throw new Error("Reducers may not dispatch actions.") } try { // 將isDispatching設(shè)置為true,表示當(dāng)次dispatch開(kāi)始 isDispatching = true // 利用傳入的reducer函數(shù)處理state和action,返回新的state // 推薦不直接修改原有的currentState currentState = currentReducer(currentState, action) } finally { // 當(dāng)次的dispatch結(jié)束 isDispatching = false } // 每次dispatch結(jié)束之后,就執(zhí)行監(jiān)聽(tīng)隊(duì)列中的監(jiān)聽(tīng)函數(shù) // 將nextListeners賦值給currentListeners,保證下一次執(zhí)行ensureCanMutateNextListeners方法的時(shí)候會(huì)重新拷貝一個(gè)新的副本 // 簡(jiǎn)單粗暴的使用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方法,顧名思義就是替換當(dāng)前的reducer處理函數(shù) 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 */ // 這個(gè)函數(shù)一般來(lái)說(shuō)用不到,他是配合其他特點(diǎn)的框架或編程思想來(lái)使用的如rx.js,感興趣的朋友可以自行學(xué)習(xí) // 這里就不多做介紹 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一個(gè)初始化的action dispatch({ type: ActionTypes.INIT }) // 最后返回這個(gè)store的所有能力 return { dispatch, subscribe, getState, replaceReducer, [$$observable]: observable } }
接下來(lái)我們看看combineReducers.js這個(gè)文件,通常我們會(huì)用它來(lái)合并我們的reducer方法
這個(gè)文件用于合并多個(gè)reducer,然后返回一個(gè)根reducer
因?yàn)閟tore中只允許有一個(gè)reducer函數(shù),所以當(dāng)我們需要進(jìn)行模塊拆分的時(shí)候,就必須要用到這個(gè)方法
// 一開(kāi)始先導(dǎo)入相應(yīng)的函數(shù) import { ActionTypes } from "./createStore" import isPlainObject from "lodash/isPlainObject" import warning from "./utils/warning" // 獲取UndefinedState的錯(cuò)誤信息 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" // 當(dāng)reducers對(duì)象是一個(gè)空對(duì)象的話,返回警告文案 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必須是一個(gè)對(duì)象 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沒(méi)有的key,因?yàn)閞edux對(duì)state分模塊的時(shí)候,是依據(jù)reducer來(lái)劃分的 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函數(shù),檢測(cè)當(dāng)遇到位置action的時(shí)候,reducer是否會(huì)返回一個(gè)undefined,如果是的話則拋出錯(cuò)誤 // 接受一個(gè)reducers對(duì)象 function assertReducerShape(reducers) { // 遍歷這個(gè)reducers對(duì)象 Object.keys(reducers).forEach(key => { const reducer = reducers[key] // 獲取reducer函數(shù)在處理當(dāng)state是undefined,actionType為初始默認(rèn)type的時(shí)候返回的值 const initialState = reducer(undefined, { type: ActionTypes.INIT }) // 如果這個(gè)值是undefined,則拋出錯(cuò)誤,因?yàn)槌跏約tate不應(yīng)該是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.` ) } // 當(dāng)遇到一個(gè)不知道的action的時(shí)候,reducer也不能返回undefined,否則也會(huì)拋出報(bào)錯(cuò) 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. */ // 導(dǎo)出combineReducers方法,接受一個(gè)參數(shù)reducers對(duì)象 export default function combineReducers(reducers) { // 獲取reducers對(duì)象的key值 const reducerKeys = Object.keys(reducers) // 定義一個(gè)最終要返回的reducers對(duì)象 const finalReducers = {} // 遍歷這個(gè)reducers對(duì)象的key for (let i = 0; i < reducerKeys.length; i++) { // 緩存每個(gè)key值 const key = reducerKeys[i] if (process.env.NODE_ENV !== "production") { if (typeof reducers[key] === "undefined") { warning(`No reducer provided for key "${key}"`) } } // 相應(yīng)key的值是個(gè)函數(shù),則將改函數(shù)緩存到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 = {} } // 定義一個(gè)變量,用于緩存錯(cuò)誤對(duì)象 let shapeAssertionError try { // 做錯(cuò)誤處理,詳情看后面assertReducerShape方法 // 主要就是檢測(cè), assertReducerShape(finalReducers) } catch (e) { shapeAssertionError = e } return function combination(state = {}, action) { // 如果有錯(cuò)誤,則拋出錯(cuò)誤 if (shapeAssertionError) { throw shapeAssertionError } if (process.env.NODE_ENV !== "production") { // 獲取警告提示 const warningMessage = getUnexpectedStateShapeWarningMessage(state, finalReducers, action, unexpectedKeyCache) if (warningMessage) { warning(warningMessage) } } // 定義一個(gè)變量來(lái)表示state是否已經(jīng)被改變 let hasChanged = false // 定義一個(gè)變量,來(lái)緩存改變后的state const nextState = {} // 開(kāi)始遍歷finalReducerKeys for (let i = 0; i < finalReducerKeys.length; i++) { // 獲取有效的reducer的key值 const key = finalReducerKeys[i] // 根據(jù)key值獲取對(duì)應(yīng)的reducer函數(shù) const reducer = finalReducers[key] // 根據(jù)key值獲取對(duì)應(yīng)的state模塊 const previousStateForKey = state[key] // 執(zhí)行reducer函數(shù),獲取相應(yīng)模塊的state const nextStateForKey = reducer(previousStateForKey, action) // 如果獲取的state是undefined,則拋出錯(cuò)誤 if (typeof nextStateForKey === "undefined") { const errorMessage = getUndefinedStateErrorMessage(key, action) throw new Error(errorMessage) } // 將獲取到的新的state賦值給新的state對(duì)應(yīng)的模塊,key則為當(dāng)前reducer的key nextState[key] = nextStateForKey // 判讀state是否發(fā)生改變 hasChanged = hasChanged || nextStateForKey !== previousStateForKey } // 如果state發(fā)生改變則返回新的state,否則返回原來(lái)的state return hasChanged ? nextState : state } }
接下來(lái)我們?cè)诳纯碽indActionCreators.js這個(gè)文件
首先先認(rèn)識(shí)actionCreators,簡(jiǎn)單來(lái)說(shuō)就是創(chuàng)建action的方法,redux的action是一個(gè)對(duì)象,而我們經(jīng)常使用一些函數(shù)來(lái)創(chuàng)建這些對(duì)象,則這些函數(shù)就是actionCreators
而這個(gè)文件實(shí)現(xiàn)的功能,是根據(jù)綁定的actionCreator,來(lái)實(shí)現(xiàn)自動(dòng)dispatch的功能
import warning from "./utils/warning" // 對(duì)于每個(gè)actionCreator方法,執(zhí)行之后都會(huì)得到一個(gè)action // 這個(gè)bindActionCreator方法,會(huì)返回一個(gè)能夠自動(dòng)執(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. */ // 對(duì)外暴露這個(gè)bindActionCreators方法 export default function bindActionCreators(actionCreators, dispatch) { // 如果傳入的actionCreators參數(shù)是個(gè)函數(shù),則直接調(diào)用bindActionCreator方法 if (typeof actionCreators === "function") { return bindActionCreator(actionCreators, dispatch) } // 錯(cuò)誤處理 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是一個(gè)對(duì)象,則獲取對(duì)象中的key const keys = Object.keys(actionCreators) // 定義一個(gè)緩存對(duì)象 const boundActionCreators = {} // 遍歷actionCreators的每個(gè)key for (let i = 0; i < keys.length; i++) { // 獲取每個(gè)key const key = keys[i] // 根據(jù)每個(gè)key獲取特定的actionCreator方法 const actionCreator = actionCreators[key] // 如果actionCreator是一個(gè)函數(shù),則直接調(diào)用bindActionCreator方法,將返回的匿名函數(shù)緩存到boundActionCreators對(duì)象中 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對(duì)象 // 用戶獲取到這個(gè)對(duì)象后,可拿出對(duì)象中的每個(gè)key的對(duì)應(yīng)的值,也就是各個(gè)匿名函數(shù),執(zhí)行匿名函數(shù)就可以實(shí)現(xiàn)dispatch功能 return boundActionCreators }
接下來(lái)我們看看applyMiddleware.js這個(gè)文件,這個(gè)文件讓redux有著無(wú)限多的可能性。為什么這么說(shuō)呢,你往下看就知道了
// 這個(gè)文件的代碼邏輯其實(shí)很簡(jiǎn)單 // 首先導(dǎo)入compose函數(shù),等一下我們會(huì)詳細(xì)分析這個(gè)compose函數(shù) 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. */ // 接下來(lái)導(dǎo)出applyMiddleware這個(gè)方法,這個(gè)方法也是我們經(jīng)常用來(lái)作為createStore中enhance參數(shù)的一個(gè)方法 export default function applyMiddleware(...middlewares) { // 首先先返回一個(gè)匿名函數(shù),有沒(méi)有發(fā)現(xiàn)這個(gè)函數(shù)跟createStore很相似啊 // 沒(méi)錯(cuò)其實(shí)他就是我們的之前看到的createStore return (createStore) => (reducer, preloadedState, enhancer) => { // 首先用原來(lái)的createStore創(chuàng)建一個(gè)store,并把它緩存起來(lái) const store = createStore(reducer, preloadedState, enhancer) // 獲取store中原始的dispatch方法 let dispatch = store.dispatch // 定一個(gè)執(zhí)行鏈數(shù)組 let chain = [] // 緩存原有store中g(shù)etState和dispatch方法 const middlewareAPI = { getState: store.getState, dispatch: (action) => dispatch(action) } // 執(zhí)行每個(gè)中間件函數(shù),并將middlewareAPI作為參數(shù)傳入,獲得一個(gè)執(zhí)行鏈數(shù)組 chain = middlewares.map(middleware => middleware(middlewareAPI)) // 將執(zhí)行鏈數(shù)組傳入compose方法中,并立即執(zhí)行返回的方法獲得最后包裝過(guò)后的dispatch // 這個(gè)過(guò)程簡(jiǎn)單來(lái)說(shuō)就是,每個(gè)中間件都會(huì)接受一個(gè)store.dispatch方法,然后基于這個(gè)方法進(jìn)行包裝,然后返回一個(gè)新的dispatch // 這個(gè)新的dispatch又作為參數(shù)傳入下一個(gè)中間件函數(shù),然后有進(jìn)行包裝。。。一直循環(huán)這個(gè)過(guò)程,直到最后得到一個(gè)最終的dispatch dispatch = compose(...chain)(store.dispatch) // 返回一個(gè)store對(duì)象,并將新的dispatch方法覆蓋原有的dispatch方法 return { ...store, dispatch } } }
看到這里,其實(shí)你已經(jīng)看完了大部分redux的內(nèi)容,最后我們看看上述文件中使用到的compose方法是如何實(shí)現(xiàn)的。
打開(kāi)compose.js,我們發(fā)現(xiàn)其實(shí)實(shí)現(xiàn)方式就是利用es5中數(shù)組的reduce方法來(lái)實(shí)現(xiàn)這種效果的
/** * 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) { // 判斷函數(shù)數(shù)組是否為空 if (funcs.length === 0) { return arg => arg } // 如果函數(shù)數(shù)組只有一個(gè)元素,則直接執(zhí)行 if (funcs.length === 1) { return funcs[0] } // 否則,就利用reduce方法執(zhí)行每個(gè)中間件函數(shù),并將上一個(gè)函數(shù)的返回作為下一個(gè)函數(shù)的參數(shù) return funcs.reduce((a, b) => (...args) => a(b(...args))) }
哈哈,以上就是今天給大家分享的redux源碼分析~希望大家能夠喜歡咯
文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請(qǐng)注明本文地址:http://systransis.cn/yun/84229.html
摘要:訂閱器不應(yīng)該關(guān)注所有的變化,在訂閱器被調(diào)用之前,往往由于嵌套的導(dǎo)致發(fā)生多次的改變,我們應(yīng)該保證所有的監(jiān)聽(tīng)都注冊(cè)在之前。 前言 用 React + Redux 已經(jīng)一段時(shí)間了,記得剛開(kāi)始用Redux 的時(shí)候感覺(jué)非常繞,總搞不起里面的關(guān)系,如果大家用一段時(shí)間Redux又看了它的源碼話,對(duì)你的理解會(huì)有很大的幫助??赐旰?,在回來(lái)看Redux,有一種 柳暗花明又一村 的感覺(jué) ,. 源碼 我分析的...
摘要:調(diào)用鏈中最后一個(gè)會(huì)接受真實(shí)的的方法作為參數(shù),并借此結(jié)束調(diào)用鏈??偨Y(jié)我們常用的一般是除了和之外的方法,那個(gè)理解明白了,對(duì)于以后出現(xiàn)的問(wèn)題會(huì)有很大幫助,本文只是針對(duì)最基礎(chǔ)的進(jìn)行解析,之后有機(jī)會(huì)繼續(xù)解析對(duì)他的封裝 前言 雖然一直使用redux+react-redux,但是并沒(méi)有真正去講redux最基礎(chǔ)的部分理解透徹,我覺(jué)得理解明白redux會(huì)對(duì)react-redux有一個(gè)透徹的理解。 其實(shí),...
摘要:函數(shù)的柯里化的基本使用方法和函數(shù)綁定是一樣的使用一個(gè)閉包返回一個(gè)函數(shù)。先來(lái)一段我自己實(shí)現(xiàn)的函數(shù)高程里面這么評(píng)價(jià)它們兩個(gè)的方法也實(shí)現(xiàn)了函數(shù)的柯里化。使用還是要根據(jù)是否需要對(duì)象響應(yīng)來(lái)決定。 奇怪,怎么把函數(shù)的柯里化和Redux中間件這兩個(gè)八竿子打不著的東西聯(lián)系到了一起,如果你和我有同樣疑問(wèn)的話,說(shuō)明你對(duì)Redux中間件的原理根本就不了解,我們先來(lái)講下什么是函數(shù)的柯里化?再來(lái)講下Redux的...
摘要:上一篇文章講解了如何使用,本篇文章將進(jìn)一步深入,從的源碼入手,深入學(xué)習(xí)的中間件機(jī)制。的功能是讓支持異步,讓我們可以在中跟服務(wù)器進(jìn)行交互等操作,而他的實(shí)現(xiàn)。。。 上一篇文章講解了redux如何使用,本篇文章將進(jìn)一步深入,從redux的源碼入手,深入學(xué)習(xí)redux的中間件機(jī)制。在這里我們會(huì)以一個(gè)redux-thunk中間件為例,逐步分解redux的中間機(jī)制如何操作,如何執(zhí)行。 閑話不多說(shuō),...
摘要:另外,內(nèi)置的函數(shù)在經(jīng)過(guò)一系列校驗(yàn)后,觸發(fā),之后被更改,之后依次調(diào)用監(jiān)聽(tīng),完成整個(gè)狀態(tài)樹(shù)的更新??偠灾?,遵守這套規(guī)范并不是強(qiáng)制性的,但是項(xiàng)目一旦稍微復(fù)雜一些,這樣做的好處就可以充分彰顯出來(lái)。 這一篇是接上一篇react進(jìn)階漫談的第二篇,這一篇主要分析redux的思想和應(yīng)用,同樣參考了網(wǎng)絡(luò)上的大量資料,但代碼同樣都是自己嘗試實(shí)踐所得,在這里分享出來(lái),僅供一起學(xué)習(xí)(上一篇地址:個(gè)人博客/s...
閱讀 1591·2021-11-23 10:01
閱讀 2978·2021-11-19 09:40
閱讀 3228·2021-10-18 13:24
閱讀 3482·2019-08-29 14:20
閱讀 2989·2019-08-26 13:39
閱讀 1283·2019-08-26 11:56
閱讀 2678·2019-08-23 18:03
閱讀 384·2019-08-23 15:35