摘要:一個沒有返回值都會有警告,所以我們寫的時候都會指定一個默認(rèn)返回值。執(zhí)行接收參數(shù),如果參數(shù)個數(shù)是,直接執(zhí)行,上文的的執(zhí)行結(jié)果返回值是一個函數(shù),作為參數(shù)的話,長度確實是,所以直接返回了,也就是,所以這塊是不需要的。
redux源碼解析 什么是redux
Redux 是 JavaScript 狀態(tài)容器,提供可預(yù)測化的狀態(tài)管理。
為什么需要使用redux提供了和雙向綁定思想不同的單向數(shù)據(jù)流,應(yīng)用狀態(tài)可以預(yù)測,可以回溯,易于調(diào)試。使用redux之初的人可能會很不適應(yīng),改變一個狀態(tài),至少寫三個方法,從這點上不如寫其他框架代碼易于理解,但是自從配合使用redux-logger一類的logger插件,就感覺到了redux的優(yōu)勢。狀態(tài)改變很清晰,很容易了解發(fā)生了什么。
源碼解析注意: 如果沒有使用過redux,建議先去看看redux文檔
api方法export { createStore, combineReducers, bindActionCreators, applyMiddleware, compose }
可以看到我們在react代碼中使用到的api,一般主動調(diào)用的就是 combineReducers ,其他部分參照例子基本可以搬過來
combineReducers打開combineReducers.js,先看export的方法,也就是combineReducers方法
var reducerKeys = Object.keys(reducers) var finalReducers = {} for (var i = 0; i < reducerKeys.length; i++) { var key = reducerKeys[i] if (process.env.NODE_ENV !== "production") { if (typeof reducers[key] === "undefined") { warning(`No reducer provided for key "${key}"`) } } if (typeof reducers[key] === "function") { finalReducers[key] = reducers[key] } }
首先看到這個函數(shù)接收的是一個對象,而這個這個對象的內(nèi)部數(shù)據(jù)值必須是一個函數(shù),不然會警告。循環(huán)了一遍這個對象,得到一個新值,對象值全部是函數(shù)的一個新reducers
var finalReducerKeys = Object.keys(finalReducers) if (process.env.NODE_ENV !== "production") { var unexpectedKeyCache = {} } var sanityError try { assertReducerSanity(finalReducers) } catch (e) { sanityError = e }
這里好像還在判斷這個最后reducers的合法性,那這里是在判斷什么呢?我們來看看 assertReducerSanity 方法
function assertReducerSanity(reducers) { Object.keys(reducers).forEach(key => { var reducer = reducers[key] var initialState = reducer(undefined, { type: ActionTypes.INIT }) 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.` ) } var 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.` ) } }) }
這塊其實就兩個判斷,reducer被執(zhí)行了兩次,一個是判斷沒有初始化state的,reducer的返回值,一個判斷action沒有type的時候的返回值。一個沒有返回值都會有警告,所以我們寫reducer的時候都會指定一個默認(rèn)返回值。
reducer會被執(zhí)行多次,這也是我們?yōu)槭裁匆WCreducer的純粹性,不能做任何其他的操作的原因
繼續(xù)往下看 combineReducers
可以看到返回了一個函數(shù) combination(state = {}, action) 。為什么返回函數(shù)呢?
那我們看 combination(state = {}, action) 像什么?不就是我們經(jīng)常寫的reducer嘛!這個reducer最終會被store傳入初始state并且當(dāng)作純函數(shù)調(diào)用,而reducer里面是可以嵌套combineReducers的結(jié)果的,所以我們在使用狀態(tài)的時候,經(jīng)常會這樣 state.user.login 這樣子的類似狀態(tài)調(diào)用
這塊想明白還是有點復(fù)雜,所有的reducer都是一個相同的函數(shù)combination,接收state參數(shù),內(nèi)部執(zhí)行同樣是combination,直到?jīng)]有combineReducers為止,才開始執(zhí)行我們自己寫的reducer函數(shù),得到的值使用combineReducers參數(shù)的對象的key作為state的key,我們自己寫的reducers執(zhí)行結(jié)果得到的值作為state的value。最終得到的就是一個巨大的Object,這就是我們的store中的state。
createStore一般這個方法我們可以直接從demo中復(fù)制過來,不需要太過了解,但是既然要深入了解redux,必然要掌握這個方法
跟之前一樣,先找到 export createStore 方法,可以看到這個函數(shù)接受三個參數(shù)
export default function createStore(reducer, preloadedState, enhancer) {
第一個reducer: 上文講到的combineReducer返回的reducer函數(shù)
第二個preloadedState:redux初始化state,可以不傳
第三個enhancer:中間件
if (typeof preloadedState === "function" && typeof enhancer === "undefined") { enhancer = preloadedState preloadedState = undefined } if (typeof enhancer !== "undefined") { if (typeof enhancer !== "function") { throw new Error("Expected the enhancer to be a function.") } return enhancer(createStore)(reducer, preloadedState) } if (typeof reducer !== "function") { throw new Error("Expected the reducer to be a function.") }
可以看到第一個判斷的意思是當(dāng)沒有第二個參數(shù)是函數(shù)的時候,默認(rèn)第二個參數(shù)就是中間件,并且默認(rèn)state置為undefined
第二個判斷的意思是當(dāng)有中間件參數(shù),但是中間參數(shù)類型不是function的時候,拋出一個非法錯誤,如果是函數(shù),先執(zhí)行中間件,退出。后續(xù)在講中間件是怎么執(zhí)行的
第三個判斷reducer是否是函數(shù),否則拋出錯誤退出
var currentReducer = reducer // 當(dāng)前reducer var currentState = preloadedState // 當(dāng)前state var currentListeners = [] // 當(dāng)前監(jiān)聽器 var nextListeners = currentListeners // 下一個監(jiān)聽器 var isDispatching = false // 重復(fù)dispatch的狀態(tài)標(biāo)記
再看看createStore的返回值
return { dispatch, subscribe, getState, replaceReducer, [$$observable]: observable }
這不是store的方法嘛,挨個看看
function getState() { return currentState }
這個沒什么好說的。
function subscribe(listener) { if (typeof listener !== "function") { throw new Error("Expected listener to be a function.") } var isSubscribed = true ensureCanMutateNextListeners() nextListeners.push(listener) return function unsubscribe() { if (!isSubscribed) { return } isSubscribed = false ensureCanMutateNextListeners() var index = nextListeners.indexOf(listener) nextListeners.splice(index, 1) } }
發(fā)布訂閱模式,熟悉事件系統(tǒng)的應(yīng)該比較明白,注冊一個方法而已,結(jié)果返回一個取消監(jiān)聽方法
function dispatch(action) { if (!isPlainObject(action)) { throw new Error( "Actions must be plain objects. " + "Use custom middleware for async actions." ) } if (typeof action.type === "undefined") { throw new Error( "Actions may not have an undefined "type" property. " + "Have you misspelled a constant?" ) } if (isDispatching) { throw new Error("Reducers may not dispatch actions.") } try { isDispatching = true currentState = currentReducer(currentState, action) } finally { isDispatching = false } var listeners = currentListeners = nextListeners for (var i = 0; i < listeners.length; i++) { listeners[i]() } return action }
老幾樣啊,先做一些判斷,我們寫代碼的時候好像沒這么嚴(yán)謹(jǐn)哈。執(zhí)行reducer,觸發(fā)所有l(wèi)isteners。這個比較簡單。
這樣子,看起來createStore沒什么復(fù)雜的,復(fù)雜的在哪呢?我們掠過的中間件退出的環(huán)節(jié)。所以來燒腦吧,看看中間件
想想我們創(chuàng)建store的時候是怎么操作的
const finalCreateStore = compose( applyMiddleware(thunk, logger) )(createStore) const store = finalCreateStore(rootReducer, initialState)
這種堆在一起的代碼不是太好看,分開,分開
const middlewares = applyMiddleware(thunk, logger) const composeResult = compose(middlewares) const finalCreateStore = composeResult(createStore) const store = finalCreateStore(rootReducer, initialState)
這就條理清晰多了,看代碼一定要看懂流程,按照順序看,不然一頭霧水,先看第一步 applyMiddleware
export default function applyMiddleware(...middlewares) { return (createStore) => (reducer, preloadedState, enhancer) => { var store = createStore(reducer, preloadedState, enhancer) var dispatch = store.dispatch var chain = [] var middlewareAPI = { getState: store.getState, dispatch: (action) => dispatch(action) } chain = middlewares.map(middleware => middleware(middlewareAPI)) dispatch = compose(...chain)(store.dispatch) return { ...store, dispatch } } }
可以看到這個方法返回一個函數(shù),既然這個函數(shù)沒有被執(zhí)行到,我們就先不看,現(xiàn)在我們得到了一個 applyMiddleware 返回的函數(shù)了
接著看 compose 方法了
export default function compose(...funcs) { if (funcs.length === 0) { return arg => arg } if (funcs.length === 1) { return funcs[0] } const last = funcs[funcs.length - 1] const rest = funcs.slice(0, -1) return (...args) => rest.reduceRight((composed, f) => f(composed), last(...args)) }
代碼更少,可是redux精髓全在這了。
compose 執(zhí)行接收參數(shù),如果參數(shù)個數(shù)是1,直接執(zhí)行,上文的 applyMiddleware 的執(zhí)行結(jié)果返回值是一個函數(shù)middlewares,作為參數(shù)的話,長度確實是1,所以直接返回了middlewares,也就是composeResult,所以這塊是不需要compose的。而這個參數(shù)函數(shù)接收一個參數(shù)就是createStore,剛好接收createStore方法,所以我們還是進(jìn)入到 applyMiddleware 的返回函數(shù)里面看看
顯然 composeResult 接收到 createStore之后返回一個函數(shù): finalCreateStore,從代碼中可以看出也是可以接收中間件方法的,不過應(yīng)該不會有人再在這里重復(fù)添加中間件了。
進(jìn)入到 finalCreateStore 中看看
創(chuàng)建了store,前文已經(jīng)講過了
把所有的middlewares執(zhí)行一遍,從這里可以看出middlewares是一個接收 { dispatch, getState } 參數(shù)的函數(shù),不可能有其他情況
把middlewares執(zhí)行的結(jié)果數(shù)組作為參數(shù)再一次傳入了compose
再次進(jìn)入到 compose 中看邏輯,如果只有一個中間件的話,同樣是把中間件直接返回,如果超過一個執(zhí)行下面的邏輯
const last = funcs[funcs.length - 1] const rest = funcs.slice(0, -1) return (...args) => rest.reduceRight((composed, f) => f(composed), last(...args))
compose 同樣只是返回了一個函數(shù)。這個函數(shù)接收的參數(shù)在 applyMiddleware 里面能看到接收到的是dispatch方法
這里巧妙的利用了js Array的reduce方法,reduce方法的原理就是回調(diào)函數(shù)的返回值作為后一個回調(diào)函數(shù)的第一個參數(shù),第一個回調(diào)函數(shù)的第一個參數(shù)的值是 reduce方法的第二個參數(shù)值。
args就是dispatch方法,這里看的出中間件函數(shù)還得返回函數(shù),這個函數(shù)得接收類似dispatch方法的函數(shù)
看看redux-chunk這個中間件的實現(xiàn)吧
function createThunkMiddleware(extraArgument) { return ({ dispatch, getState }) => next => action => { if (typeof action === "function") { return action(dispatch, getState, extraArgument); } return next(action); }; } const thunk = createThunkMiddleware(); thunk.withExtraArgument = createThunkMiddleware; export default thunk;
看到 next 方法,使用過express的同學(xué)應(yīng)該會很熟悉,這個next和express的next很像,原理也類似。
每個中間件的最后一層函數(shù)都是一個next,才可以在reduce里面作為參數(shù)傳遞,才可以實現(xiàn)中間件的傳遞
這也是redux名稱的由來。
redux代碼短小精悍,設(shè)計精巧,真好。
文章版權(quán)歸作者所有,未經(jīng)允許請勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請注明本文地址:http://systransis.cn/yun/82630.html
摘要:總體概括是官方推薦的一個狀態(tài)管理庫。功能強(qiáng)大且代碼優(yōu)雅。在閱讀源碼的過程中可以看出,其只依賴這兩個庫的某幾個方法。從這里來看,可以看作是無依賴的一個庫。這就是對源碼的整體概括,水平有限,歡迎拍磚。后續(xù)的源碼解讀和測試?yán)涌梢躁P(guān)注源碼解讀倉庫 Redux總體概括 redux是react官方推薦的一個狀態(tài)管理庫。功能強(qiáng)大且代碼優(yōu)雅。從package.json文件中: dependencie...
摘要:下面會從淺到深,淡淡在閱讀源碼過程中自己的理解。分拆子頁面后,每一個子頁面對應(yīng)一個文件??偨Y(jié)上面就是最早版本的源碼,很簡潔的使用了等其目的也很簡單簡化相關(guān)生態(tài)的繁瑣邏輯參考源碼地址 ??dva的思想還是很不錯的,大大提升了開發(fā)效率,dva集成了Redux以及Redux的中間件Redux-saga,以及React-router等等。得益于Redux的狀態(tài)管理,以及Redux-saga中...
摘要:下面會從淺到深,淡淡在閱讀源碼過程中自己的理解。分拆子頁面后,每一個子頁面對應(yīng)一個文件??偨Y(jié)上面就是最早版本的源碼,很簡潔的使用了等其目的也很簡單簡化相關(guān)生態(tài)的繁瑣邏輯參考源碼地址 ??dva的思想還是很不錯的,大大提升了開發(fā)效率,dva集成了Redux以及Redux的中間件Redux-saga,以及React-router等等。得益于Redux的狀態(tài)管理,以及Redux-saga中...
摘要:下面會從淺到深,淡淡在閱讀源碼過程中自己的理解。分拆子頁面后,每一個子頁面對應(yīng)一個文件??偨Y(jié)上面就是最早版本的源碼,很簡潔的使用了等其目的也很簡單簡化相關(guān)生態(tài)的繁瑣邏輯參考源碼地址 ??dva的思想還是很不錯的,大大提升了開發(fā)效率,dva集成了Redux以及Redux的中間件Redux-saga,以及React-router等等。得益于Redux的狀態(tài)管理,以及Redux-saga中...
摘要:到月底了,小明的爸爸的單位發(fā)了工資總計塊大洋,拿到工資之后第一件的事情就是上交,毫無疑問的,除非小明爸爸不要命了。當(dāng)小明的爸爸收到這個通知之后,心的一塊大石頭也就放下來了。下面我們正式開始我們的源碼閱讀之旅。 前言 用過react的小伙伴對redux其實并不陌生,基本大多數(shù)的React應(yīng)用用到它。一般大家用redux的時候基本都不會單獨去使用它,而是配合react-redux一起去使用...
摘要:主模塊的入口模塊就是。主要就做兩件事引入個功能模塊,并掛載至同一個對象上,對外暴露。在非環(huán)境下壓縮代碼,給予警告。后續(xù)的源碼解讀和測試?yán)涌梢躁P(guān)注源碼解讀倉庫 主模塊 redux的入口模塊就是src/index.js。這個文件的代碼十分簡單。主要就做兩件事: 引入個功能模塊,并掛載至同一個對象上,對外暴露。 在非production環(huán)境下壓縮代碼,給予警告。 下面是模塊的源碼(只包...
閱讀 2040·2023-04-26 02:15
閱讀 2309·2021-11-19 09:40
閱讀 1051·2021-10-27 14:13
閱讀 3322·2021-08-23 09:44
閱讀 3622·2019-12-27 12:24
閱讀 661·2019-08-30 15:53
閱讀 1175·2019-08-30 10:53
閱讀 2167·2019-08-26 12:14