寫在開頭
前置知識內(nèi)容,閉包,高階函數(shù)检痰,函數(shù)式編程思想愤惰,redux核心概念苇经。
redux文檔:https://www.redux.org.cn
redux源碼:https://github.com/reduxjs/redux
克隆代碼
git clone https://github.com/reduxjs/redux.git
本文對應(yīng)redux版本為redux v4.0.1
一、結(jié)構(gòu)
在redux的src目錄下可以清楚的看到redux的幾個核心文件和js工具函數(shù)宦言。通過閱讀index.js
文件可以清楚的看到redux
導(dǎo)出的5個核心方法
文件 | 功能 | |
---|---|---|
index |
redux的入口文件 | 用法 |
createStore |
提供核心APIcreateStore 根據(jù)reducer扇单,preState,applyMiddleware奠旺。創(chuàng)建并返回store |
|
combineReducers |
提供核心APIcombineReducers ,用于合并拆分的reducer |
|
bindActionCreators |
提供核心APIbindActionCreators 可以簡化dispatch action的調(diào)用方法蜘澜。 |
|
applyMiddleware |
提供核心APIapplyMiddleware 。 |
|
compose | ||
actionTypes.js |
內(nèi)置的action.type响疚。 | |
isPlainObject.js |
判斷是否是簡單對象鄙信。 | |
warning.js |
用于輸出警告信息。 |
二忿晕、工具文件
2.1 actionTypes
源碼截取
const randomString = () =>
Math.random()
.toString(36)
.substring(7)
.split('')
.join('.');
const ActionTypes = {
INIT: `@@redux/INIT${randomString()}`,
REPLACE: `@@redux/REPLACE${randomString()}`,
PROBE_UNKNOWN_ACTION: () => `@@redux/PROBE_UNKNOWN_ACTION${randomString()}`
};
export default ActionTypes
actionTypes文件主要封裝了redux內(nèi)置的actionType装诡,其中ActionTypes.INIT
主要用于初始化store所使用。REPLACE
PROBE_UNKNOWN_ACTION
為替換reducer的actionType。
2.2isPlainObject
源碼截取
export default function isPlainObject(obj) {
if (typeof obj !== 'object' || obj === null) return false
let proto = obj
while (Object.getPrototypeOf(proto) !== null) {
proto = Object.getPrototypeOf(proto)
}
return Object.getPrototypeOf(obj) === proto
}
本段函數(shù)主要用于對 reducer函數(shù)的action參數(shù)進(jìn)行校驗(yàn)鸦采。函數(shù)用于判斷一個對象是否是一個簡單對象宾巍,簡單對象是指直接使用對象字面量{}
或者new Object()
、Object.create(null)
所創(chuàng)建的對象渔伯。jQuery和lodash等JavaScript函數(shù)均對此有所實(shí)現(xiàn).redux的老期版本使用了ladash的實(shí)現(xiàn)版本顶霞,后改為自己實(shí)現(xiàn)。
2.3warning
源碼截仍酆怠:
export default function warning(message) {
if (typeof console !== 'undefined' && typeof console.error === 'function') {
console.error(message)
}
try {
// This error was thrown as a convenience so that if you enable
// "break on all exceptions" in your console,
// it would pause the execution at this line.
throw new Error(message)
} catch (e) {
}
}
本函數(shù)主要用于對代碼執(zhí)行過程中所遇到的錯誤進(jìn)行統(tǒng)一處理确丢,在控制臺打印錯誤原因。使用throw new Error(message)是為了方便調(diào)試時中斷執(zhí)行
三吐限、核心文件
3.1 createStore.js
使用場景
// reducer是必傳的函數(shù)主要用于響應(yīng)action對store做處理鲜侥。
// preloadedState是一個可選對象,用于指定redux中store的默認(rèn)值诸典,使用場景比如說描函,服務(wù)端渲染是redux數(shù)據(jù)的注入,或者應(yīng)用程序頁面刷新時redux數(shù)據(jù)的保留狐粱。
// enhancer 用于增強(qiáng)redux的功能舀寓,比如處理異步,打印log等等
createStore(reducer, [preloadedState], enhancer)
creeateStore.js主要暴露了一個函數(shù)肌蜻,即createStore函數(shù)互墓。整個函數(shù)除去注釋,約為180行左右蒋搜。
下面將對此函數(shù)進(jìn)行分析篡撵。此函數(shù)的參數(shù)為(rducer,preloadState,enhancer)
經(jīng)過處理返回一個store對象豆挽,store對象的值如下育谬。
{
dispatch,
subscribe,
getState,
replaceReducer,
[$$observable]: observable
}
createStore函數(shù)主要有5個變量用來存儲信息,通過變量名,我們可以很容易的知道每個變量的含義帮哈。createStore通過閉包將這些變量存儲起來膛檀,通過store的屬性來操作數(shù)據(jù)。
let currentReducer = reducer
let currentState = preloadedState
let currentListeners = []
let nextListeners = currentListeners
let isDispatching = false
源代碼行數(shù)過多娘侍,在這里就不體現(xiàn)出來了咖刃,點(diǎn)擊這里查看
下面來逐行分析createStore函數(shù)的執(zhí)行過程
1.校驗(yàn)函數(shù)傳入的參數(shù)
函數(shù)執(zhí)行的第一步是函數(shù)校驗(yàn),如果校驗(yàn)未通過憾筏,直接拋出錯誤僵缺。
函數(shù)的第一個參數(shù)是必傳的函數(shù)(reducers),函數(shù)的額第二個參數(shù)是可選的除函數(shù)外的任意類型參數(shù)preloadState
踩叭,參數(shù)的第三個參數(shù)是可選的函數(shù)enhancer
(applyMiddleWare函數(shù)返回的函數(shù)組成的數(shù)組)磕潮。也可以傳遞兩個參數(shù)reducers
和enhancer
翠胰。
函數(shù)校驗(yàn)了四個地方
第一步,由于函數(shù)可以支持多個中間件函數(shù)自脯,但是多個中間件必須要通過compose
函數(shù)包裝之后在可以傳入之景。函數(shù)首先對此進(jìn)行了校驗(yàn)。判斷如果函數(shù)的第二個和第三個參數(shù)都為函數(shù)膏潮《凸罚或者函數(shù)的第三個參數(shù)和第四個參數(shù)都為函數(shù)。則可以猜測到焕参,開發(fā)者可能是為使用compose包裝多個中間件轻纪,所導(dǎo)致,所以此時函數(shù)會拋出錯誤信息叠纷。提示出當(dāng)前可能的錯誤原因刻帚。
第二步,對于函數(shù)只傳入reducers
和enhancer
進(jìn)行了處理涩嚣。判斷傳入的第二個參數(shù)是函數(shù)崇众,并且沒有傳遞第三個參數(shù),此時將preloadState賦值給enhancer航厚。將preloadState置空顷歌。是實(shí)參與形參相對應(yīng)。
第三步幔睬,判斷enhancer是否存在眯漩,如果不是函數(shù),就拋出錯誤麻顶。如果是函數(shù)則直接返回enhancer(createStore)(reducer, preloadedState)
,這里可能不好理解坤塞,需要參照applyMiddleWare函數(shù)部分源碼。
第四步澈蚌,判斷傳入的reducer參數(shù)的類型是否是函數(shù),如果不是函數(shù)灼狰,拋出錯誤宛瞄。
到這里,函數(shù)參數(shù)的校驗(yàn)就結(jié)束了交胚。
2.定義函數(shù)內(nèi)的局部變量來存儲傳入的參數(shù)和后續(xù)會用到的狀態(tài)等信息
let currentReducer = reducer //傳入的reducer參數(shù)
let currentState = preloadedState //當(dāng)前store里的state
let currentListeners = []//當(dāng)前監(jiān)聽函數(shù)數(shù)組
let nextListeners = currentListeners //接下來的監(jiān)聽函數(shù)數(shù)組
let isDispatching = false //是否正在dispatch
3.定義六個函數(shù)來對來處理store的數(shù)據(jù)交互
第一個函數(shù):ensureCanMutateNextListeners
function ensureCanMutateNextListeners() {
if (nextListeners === currentListeners) {
nextListeners = currentListeners.slice()
}
}
此函數(shù)的功能較為簡單份汗,就不在說明
第二個函數(shù):getState
function getState() {
if (isDispatching) {
throw new Error('msg')
}
return currentState
}
此函數(shù)就是最終暴露出來的store.dispatch
函數(shù),函數(shù)首先判斷當(dāng)前是不是處于dispatching狀態(tài)蝴簇。是的話直接拋出錯誤杯活。否則就將函數(shù)的局部變量current返回。
第三個函數(shù):subscribe
function subscribe(listener) {
if (typeof listener !== 'function') {
throw new Error('msg')
}
if (isDispatching) {
throw new Error('msg')
}
let isSubscribed = true;
ensureCanMutateNextListeners();
nextListeners.push(listener);
return function unsubscribe() {
if (!isSubscribed) {
return
}
if (isDispatching) {
throw new Error('msg')
}
isSubscribed = false;
ensureCanMutateNextListeners();
const index = nextListeners.indexOf(listener);
nextListeners.splice(index, 1)
}
}
此函數(shù)也是最終暴露出來的store.subscribe
函數(shù)熬词,此函數(shù)用于添加一個訂閱state
變化的函數(shù)旁钧。傳入?yún)?shù)為函數(shù)吸重,如果傳入非函數(shù),或者正在dispatching時調(diào)用歪今,會拋出錯誤嚎幸。函數(shù)返回了一個函數(shù)用于取消訂閱。
接下來逐步分析此函數(shù)的執(zhí)行過程寄猩,首先參數(shù)和當(dāng)前狀態(tài)的校驗(yàn)嫉晶。然后定義局部變量標(biāo)記是否已經(jīng)訂閱并標(biāo)記為true,然后調(diào)用之前的ensureCanMutateNextListeners()
函數(shù)田篇。以確保nextListeners
與currentListeners相同替废。然后將傳入的listener函數(shù)添加到nextListeners
數(shù)組。隨后返回一個新的函數(shù)用于取消訂閱泊柬。
在返回的新的函數(shù)中椎镣,首先做了狀態(tài)的判斷,如果正在dispatching彬呻,那么拋出錯誤衣陶,通過之前定義的是否訂閱標(biāo)記判斷,當(dāng)前的listener是否還在被訂閱闸氮。如果已經(jīng)取消了訂閱剪况,那么直接返回,(這里主要處理了取消訂閱函數(shù)可以被多次調(diào)用從而產(chǎn)生錯誤的情況蒲跨。這里使用了閉包)否則译断,將取消訂閱的標(biāo)記置為false。再此執(zhí)行ensureCanMutateNextListeners()
或悲,原因同上孙咪。然后使用數(shù)組的indexOf方法在當(dāng)前的
nextListeners
數(shù)組中找出對應(yīng)的索引。使用數(shù)組的splice
方法將其刪除巡语。
第四個函數(shù):dispatch
function dispatch(action) {
if (!isPlainObject(action)) {
throw new Error('msg' )
}
if (typeof action.type === 'undefined') {
throw new Error('msg')
}
if (isDispatching) {
throw new Error('msg')
}
try {
isDispatching = true;
currentState = currentReducer(currentState, action)
} finally {
isDispatching = false
}
const listeners = (currentListeners = nextListeners);
for (let i = 0; i < listeners.length; i++) {
const listener = listeners[i];
listener()
}
return action
}
這個函數(shù)也是最終暴露出來的store.dispatch
方法翎蹈。主要用于觸發(fā)action以修改state,是非常重要的幾個函數(shù)之一男公。函數(shù)執(zhí)行過程中荤堪。首先校驗(yàn)參數(shù)action必須為普通javaScript
對象,且action.type必須不能為undefined枢赔,并且當(dāng)前不能處于dispatching狀態(tài)澄阳。否則就會拋出錯誤。校驗(yàn)通過后踏拜。將isDispatching置為true碎赢,通過執(zhí)行currentState = currentReducer(currentState, action)
更改state,此時便成功的觸發(fā)了一個action。在執(zhí)行完成之后速梗,將isDispatching
置為false肮塞。隨后依次執(zhí)行訂閱此store的函數(shù)襟齿。代碼如下
const listeners = (currentListeners = nextListeners);
for (let i = 0; i < listeners.length; i++) {
const listener = listeners[i];
listener()
}
最后將action參數(shù)返回
第五個函數(shù):replaceReducer
function replaceReducer(nextReducer) {
if (typeof nextReducer !== 'function') {
throw new Error('Expected the nextReducer to be a function.')
}
currentReducer = nextReducer;
dispatch({ type: ActionTypes.REPLACE })
}
此函數(shù)也是直接暴露出來的store.replaceReducer
函數(shù),一般在實(shí)際開發(fā)中使用較少峦嗤。 主要用于動態(tài)加載reducer
蕊唐。
第六個函數(shù):observable
function observable() {
const outerSubscribe = subscribe;
return {
subscribe(observer) {
if (typeof observer !== 'object' || observer === null) {
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
}
}
}
接下來createStore
函數(shù)將直接返回已經(jīng)創(chuàng)建好的函數(shù)。
return {
dispatch,
subscribe,
getState,
replaceReducer,
[$$observable]: observable
}
3.2 combineReducers.js
combineReducers.js
文件在去除注釋和錯誤提示的情況下烁设,代碼行數(shù)約為130行左右替梨。查看源碼。此js文件中主要有4個函數(shù)
函數(shù)名 | 作用 | 備注 |
---|---|---|
getUndefinedStateErrorMessage |
供redux內(nèi)部調(diào)用装黑,根絕action和key生成錯誤信息副瀑,例如reduce函數(shù)返回undefined時的情況 | |
getUnexpectedStateShapeWarningMessage |
供redux內(nèi)部調(diào)用,用于獲取警告信息恋谭,主要進(jìn)行了 reducer的判空糠睡,當(dāng)前的state是否為簡單對象,給state中存在而reducer中不存在的屬性添加緩存標(biāo)識疚颊,并返回警告信息狈孔。 | |
assertReducerShape |
檢測用于組合的reducer是否符合redux對頂?shù)膔educer | |
combineReducers |
直接暴露出來,用于合并reducer |
assertReducerShape材义,主要對于傳入的reducer數(shù)組進(jìn)行便利進(jìn)行檢驗(yàn)均抽,首先調(diào)用reducer(undefined,{ type: ActionTypes.INIT })
以獲取initState
,initState
如果是undefined
則拋出錯誤信息,提示必須返回初始state其掂,如果不想為這個reducer設(shè)置值油挥,要返回null而不是undefined。如果沒有出錯款熬,則調(diào)用reducer(undefined, { type: ActionTypes.PROBE_UNKNOWN_ACTION() })
,通過未知的action深寥,來檢測reducer能否正確處理,即返回的值類型是否為undefined
,如果是則拋出錯誤原因贤牛。
combineReducers惋鹅, 函數(shù)首先對傳入的reducers對象進(jìn)行遍歷,將結(jié)果賦值到局部變量finalReducers
殉簸。如果不是正式環(huán)境闰集,那么對于為null的reducer進(jìn)行提示,如果是正式環(huán)境喂链,則忽略類型部位函數(shù)的reducer。然后調(diào)用assertReducerShape對finalReducers
進(jìn)行類型校驗(yàn)妥泉,并存儲錯誤信息到局部變量shapeAssertionError椭微。然后反對一個合并完成的reducer函數(shù)。
在新返回的函數(shù)中盲链,首先判斷shapeAssertionError蝇率,如果存在錯誤就拋出迟杂,對于非正式環(huán)境,使用getUnexpectedStateShapeWarningMessage
進(jìn)行校驗(yàn)本慕,并提醒錯誤排拷。定義一個標(biāo)記表示state是否已經(jīng)變化并置為false,定義下一個狀態(tài)的結(jié)果nextState
,接下來遍歷執(zhí)行reducer锅尘。最終將nextState返回
let hasChanged = false
const nextState = {}
for (let i = 0; i < finalReducerKeys.length; i++) {
const key = finalReducerKeys[i]
const reducer = finalReducers[key]
const previousStateForKey = state[key]
const nextStateForKey = reducer(previousStateForKey, action)
if (typeof nextStateForKey === 'undefined') {
const errorMessage = getUndefinedStateErrorMessage(key, action)
throw new Error(errorMessage)
}
nextState[key] = nextStateForKey
hasChanged = hasChanged || nextStateForKey !== previousStateForKey
}
return hasChanged ? nextState : state
3.3 applyMiddleware.js
源碼截取
import compose from './compose'
export default function applyMiddleware(...middlewares) {
return createStore => (...args) => {
const store = createStore(...args);
let dispatch = () => {
throw new Error(
'Dispatching while constructing your middleware is not allowed. ' +
'Other middleware would not be applied to this dispatch.'
)
};
const middlewareAPI = {
getState: store.getState,
dispatch: (...args) => dispatch(...args)
};
const chain = middlewares.map(middleware => middleware(middlewareAPI));
dispatch = compose(...chain)(store.dispatch);
// 注意由于函數(shù)說引用類型所以此時middlewareAPI的dispatch函數(shù)也一同更改监氢。
return {
...store,
dispatch
}
}
}
applyMiddleware函數(shù)主要是與中間件有關(guān)的函數(shù),他允許我們在action到達(dá)reducer之前對action進(jìn)行加工處理藤违。
使用
// createStore中對于中間件的調(diào)用
return enhancer(createStore)(reducer, preloadedState)
//applyMiddleware函數(shù)的使用浪腐;
let store=createStore(reducer,preloadStste,applyMiddleware(thunk))
// redux-thunk源碼
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;
這里部分源碼設(shè)計(jì)到了高階函數(shù),初次閱讀可能不太好理解顿乒,所以將于applyMiddleware有關(guān)的內(nèi)容均列在了上面议街。由此來梳理applyMiddleware函數(shù)的工作流程。
在創(chuàng)建store時璧榄,首先調(diào)用createStore方法特漩,第三個參數(shù)為applyMiddleware
函數(shù)返回的新的函數(shù)(起名字為函數(shù)1,源碼中第一個箭頭函數(shù))骨杂。在createStore
源碼中可以看到涂身,檢測到第三個參數(shù)數(shù)為函數(shù)時將會執(zhí)行此函數(shù)(函數(shù)1),并且把createStore
函數(shù)作為函數(shù)1的參數(shù)腊脱。函數(shù)1的執(zhí)行會返回一個新的函數(shù)(函數(shù)2访得,源碼中第二個箭頭函數(shù))。通過createStore
源碼可以看到陕凹,此時再次對函數(shù)二悍抑,進(jìn)行了執(zhí)行,傳入的參數(shù)時reducer和preloadState杜耙。此時便開始執(zhí)行applyMiddleware函數(shù)的主體部分搜骡。
在applyMiddleware中可以看到,首先根據(jù)傳入的reducer和preloadState創(chuàng)建了store佑女。然后定義了一個dispatch函數(shù)记靡。作用是執(zhí)行時的校驗(yàn),避免在執(zhí)行函數(shù)時dispaching
团驱。然后創(chuàng)建了middlewareAPI
對象供中間件函數(shù)使用摸吠。然后將middlewareAPI
作為參數(shù),便利執(zhí)行中間件數(shù)組函數(shù)嚎花。將返回的結(jié)果存儲在chain數(shù)組寸痢。通過thunk函數(shù)的源碼使得我們可以了解到此時存儲在chain書中的的每一項(xiàng)仍然是一個函數(shù),函數(shù)接收的形參為next紊选。隨后將store.dispatch作為參數(shù)執(zhí)行chain數(shù)組中的每一個函數(shù)啼止,具體為首先執(zhí)行第一個函數(shù)道逗,將store.dispatch作為實(shí)參傳入。將其返回的結(jié)果作為第二個函數(shù)的參數(shù)献烦,以此類推滓窍,將最后結(jié)果賦值給dispatch。根絕thunk的源碼可以看到巩那,對于chain數(shù)組的每一項(xiàng)吏夯,執(zhí)行后仍然會返回一個新的函數(shù),這個新的函數(shù)的形參為action,這個新的函數(shù)恰恰就是我們要自己開發(fā)的中間件函數(shù)拢操。最后將store和dispatch返回锦亦。此時回到createStore
源碼,可以看到令境,返回的恰恰就是最終創(chuàng)建的store杠园。
現(xiàn)在已經(jīng)梳理創(chuàng)建含有中間件的store的函數(shù)執(zhí)行過程。下面來分析一下store.dispatch
這個過程舔庶。
接著上面的來說抛蚁,最終返回了dispatch函數(shù)并且作為了store的一個屬性。當(dāng)我們觸發(fā)action的時候惕橙,顯然使用的就是這個dispatch函數(shù)∏扑Γ現(xiàn)在我們來看看這個dispatch函數(shù)是什么樣子的。
由于之前使用了compose函數(shù)弥鹦,所以這部分呢可能不太容易理解肚逸,此時我們,假設(shè)兩個中間件函數(shù)第一個為上面所提到的thunk
中間件彬坏,假設(shè)第二個為打印log的中間件,經(jīng)過map處理后的如下朦促。
const logger = store => next => action => {
console.group(action.type)
console.info('dispatching', action)
let result = next(action)
console.log('next state', store.getState())
console.groupEnd(action.type)
return result
}
const thunk = ({ dispatch, getState }) => next => action => {
if (typeof action === 'function') {
return action(dispatch, getState, extraArgument);
}
return next(action);
};
為了利于理解我們首先考慮只有一個中間件的情況,此時
dispatch=componse([logger(middlewareAPI)])(store.dispatch)
//等同于
dispatch=logger(middlewareAPI)(store.dispatch)
// 所以
store.dispatch(action)=logger(middlewareAPI)(store.dispatch)(action)
// 此時發(fā)現(xiàn)
logger(middlewareAPI)(store.dispatch)(action) 與定義時的剛好對應(yīng)
考慮有多個中間件的情況栓始。
dispatch=componse([logger(middlewareAPI),thunk(middlewareAPI)])(store.dispatch)
執(zhí)行流程分析
- 根據(jù)compose函數(shù)的源碼可以將上面表達(dá)式轉(zhuǎn)化為
dispatch=thunk(middlewareAPI)(logger((middlewareAPI)(store.dispatch)))
- 首先執(zhí)行l(wèi)ogger(middlewareAPI)(store.dispatch)函數(shù)务冕,函數(shù)返回一個類似于next=>action=>{} 的函數(shù)。執(zhí)行這個函數(shù)幻赚,傳入的參數(shù)next的值恰好是store,dispatch禀忆。
- 然后執(zhí)行thunk函數(shù),函數(shù)也返回一個類似于next=>action=>{} 的函數(shù)落恼。thunk()函數(shù)執(zhí)行時傳入的是logger函數(shù)返回的函數(shù)箩退。
- 多個中間件以此類似
- 當(dāng)?shù)阶詈笠粋€中間件時,返回類似于next=>action=>{}的函數(shù)佳谦。next參數(shù)是倒數(shù)第二個中間件返回的函數(shù)戴涝。最后一個函數(shù)返回的函數(shù)被賦值給了dispatch
總結(jié):通過閉包存儲了最新的store值。通過compose函數(shù),使得每個中間件的next參數(shù)指向其后面的中間件函數(shù)喊括。最后一個中間件指向store.dispatch。當(dāng)觸發(fā)action時矢棚,action會依次的經(jīng)過中間件的處理郑什。在每個中間件中可以通過store.getState()取得最新的state值,通過dispatch可以從第一個中間件觸發(fā)dispatch()蒲肋。通過調(diào)用next(action)觸發(fā)下一個中間件函數(shù)
dispatch = compose(...chain)(store.dispatch)
compose函數(shù)
函數(shù)傳入的數(shù)組的每一項(xiàng)是形如next=>action=>{}的函數(shù)蘑拯。
作用
- 每一個函數(shù)的next參數(shù)是對他之后函數(shù)的返回值
- 最后一個函數(shù)的action是store.dispatch
- 最后的返回值是第一個函數(shù)的返回值,賦值給dispatch
- 每次調(diào)用dispatch兜粘,就是調(diào)用第一個中間件
- 在中間件函數(shù)內(nèi)調(diào)用next就是調(diào)用下一個中間件的
(action)=>{}
- 調(diào)用最后一個中間件的next申窘,會調(diào)用store.dispatch,更新state。
接著從applyMiddleware
函數(shù)源碼compose
部分開始分析孔轴。此時的dispatch函數(shù)如下
dispatch=componse([chainOne,chainTwo])(store.dispatch)
根據(jù)compose源碼剃法。
dispatch=chainTwo(chainOne(store.dispatch))
3.4 bindActionCreators.js
源碼截取
function bindActionCreator(actionCreator, dispatch) {
return function () {
return dispatch(actionCreator.apply(this, arguments))
}
}
export default function bindActionCreators(actionCreators, dispatch) {
if (typeof actionCreators === 'function') {
return bindActionCreator(actionCreators, dispatch)
}
// 校驗(yàn)參數(shù)必須為對象
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"?`
)
}
const boundActionCreators = {}
for (const key in actionCreators) {
const actionCreator = actionCreators[key]
if (typeof actionCreator === 'function') {
boundActionCreators[key] = bindActionCreator(actionCreator, dispatch)
}
}
return boundActionCreators
}
在redux中改變state只能通過store.dispatch(action)
的方式,這樣迫使我們不得不將store.dispatch
函數(shù)進(jìn)行逐層傳遞路鹰,這樣會增加一些無用的重復(fù)代碼贷洲。
這時就需要使用bindActionCreators
來加工actionCreators
函數(shù)。bindActionCreators(actionCreators, dispatch)
返回一個與原函數(shù)/對象相同的新函數(shù)晋柱。因?yàn)檫^閉包保存了store.dispatch
并且通過apply
調(diào)整了this的指向优构。直接執(zhí)行返回的函數(shù)/對象的屬性便可以觸發(fā)數(shù)據(jù)的改變,使得我們不在需要將dispatch逐層傳遞雁竞,也使得我們可以像執(zhí)行普通函數(shù)一樣來觸發(fā)action钦椭。
3.5 compose.js
源碼截取
export default function compose(...funcs) {
if (funcs.length === 0) {
return arg => arg
}
if (funcs.length === 1) {
return funcs[0]
}
return funcs.reduce((a, b) => (...args) => a(b(...args)))
}
函數(shù)的作用為將多個函數(shù)組合成一個函數(shù)。
使得compose(f,g,h)(...arg)
等同于(...arg)=>f(g(h(...arg)))
在源碼的applyMiddleware函數(shù)中使用了此函數(shù)碑诉。
四彪腔、總結(jié)
以上這些內(nèi)容就是我對于redux的部分理解。
redux簡單的說就是一個狀態(tài)管理工具联贩。也可以與除了react之外的其他框架組合使用漫仆,比如說Vue.js
,當(dāng)然選擇Vuex對于Vue是更好的選擇。對于React來說redux也不是其唯一的狀態(tài)管理工具泪幌,除此之外也有dva盲厌,mobox
五、寫在最后
以上這些內(nèi)容就是我對于redux的部分理解祸泪。從開始學(xué)習(xí)至文章產(chǎn)出大約一周左右吗浩。由于個人能力有限,所以可能有很多的不足之處没隘。當(dāng)然這篇文章也會不斷的更新懂扼,完善。
預(yù)告下一篇不定時更新文章,redux有關(guān)部分框架的源碼解析阀湿,比如react-redux
,react-saga
,reacr-router-redux
赶熟。
推薦下載源碼進(jìn)行閱讀學(xué)習(xí),相信會有很多的收獲陷嘴,加油