Redux源碼剖析

前面寫了《React組件性能優(yōu)化》《Redux性能優(yōu)化》《React-Redux性能優(yōu)化》,但是都沒(méi)有從這些框架的實(shí)現(xiàn)上講為什么?這次就從源碼上來(lái)分析一下這些框架的實(shí)現(xiàn)原理,以更深入的理解這些框架,并更好的使用它們。

Redux的api很簡(jiǎn)單,下面一個(gè)一個(gè)的分析。
createStore


首先說(shuō)下它的三個(gè)參數(shù)reducer、preloadedState、enhancer。reducer是唯一必傳的參數(shù),它很重要,因?yàn)樗鼪Q定了整個(gè)state。preloadedState就是state的初始值。第三個(gè)參數(shù)不是特別常用,它是個(gè)函數(shù),如果它存在的情況下,會(huì)執(zhí)行下面的語(yǔ)句:

enhancer(createStore)(reducer, preloadedState)

很顯然enhancer和middleware作用很像,用來(lái)增強(qiáng)store。

createStore內(nèi)部維護(hù)了currentReducer(當(dāng)前的reducer)和currentState(當(dāng)前的state),初始化的時(shí)候:

currentReducer = reducer
currentState = preloadedState

這兩個(gè)變量很重要,因?yàn)楹芏嗖僮鞫己退鼈兿嚓P(guān)。

subscribe

在createStore內(nèi)部維護(hù)了兩個(gè)數(shù)組currentListeners、nextListeners。nextListeners的存在是為了避免在listeners執(zhí)行過(guò)程中,listeners發(fā)生改變,導(dǎo)致錯(cuò)誤。listeners的添加或刪除都是對(duì)nextListeners進(jìn)行操作的。

nextListeners = currentListeners.slice()

保證nextListeners的修改不會(huì)影響currentListeners。

subscribe(listener)的返回值是個(gè)函數(shù),執(zhí)行這個(gè)函數(shù)就會(huì)unsubscribe(listener),取消監(jiān)聽(tīng)。

dispatch

很重要,因?yàn)橹荒芡ㄟ^(guò)它修改state,它只接收一個(gè)參數(shù)action,action必須是簡(jiǎn)單對(duì)象,而且必須有type屬性。在dispatch內(nèi)部關(guān)鍵代碼是:

currentState = currentReducer(currentState, action)

var listeners = currentListeners = nextListeners
for (var i = 0; i < listeners.length; i++) {
  listeners[i]()
}

第一行用來(lái)調(diào)用reducer,并將執(zhí)行的結(jié)果賦給currentState。這樣currentState中的數(shù)據(jù)就總是最新的,即reducer處理完action之后返回的數(shù)據(jù)。沒(méi)有條件,執(zhí)行dispatch后reducer總會(huì)執(zhí)行。

后面3行代碼是用來(lái)調(diào)用subscribe傳進(jìn)來(lái)的listeners,按順序執(zhí)行它們,沒(méi)有任何條件判斷,也就是說(shuō)只要執(zhí)行dispatch,所有的listeners都會(huì)執(zhí)行,不管state有沒(méi)有發(fā)生改變,而且listeners執(zhí)行的時(shí)候是沒(méi)參數(shù)的。

Redux內(nèi)部有個(gè)變量

ActionTypes = {
  INIT: '@@redux/INIT'
}

在創(chuàng)建store(即調(diào)用createStore(reducer, preloadedState, enhancer))的時(shí)候會(huì)執(zhí)行

dispatch({ type: ActionTypes.INIT })

所以reducer和listeners在store創(chuàng)建的時(shí)候都會(huì)被執(zhí)行一遍,listeners沒(méi)有什么特別要關(guān)注的。reducer執(zhí)行必須關(guān)注,因?yàn)樗膱?zhí)行結(jié)果影響你的數(shù)據(jù)。比如createStore的時(shí)候你沒(méi)有傳入preloadedState,在reducer內(nèi)的state有默認(rèn)參數(shù),正常情況下你的reducer會(huì)使用default分支處理這個(gè)action,而且一般default分支會(huì)直接返回state,所以這種情況下store創(chuàng)建完后,使用getState()獲取的值就是默認(rèn)參數(shù)組成的state。

getState

獲取store中的state,很簡(jiǎn)單,主要代碼:

return currentState

在沒(méi)有dispatch正在執(zhí)行的情況下,直接返回前面說(shuō)很重要的currentState。

replaceReducer

參數(shù)是nextReducer,也很簡(jiǎn)單,關(guān)鍵代碼:

currentReducer = nextReducer
dispatch({ type: ActionTypes.INIT })

直接拿nextReducer替換掉前面說(shuō)很重要的currentReducer,后面再執(zhí)行dispatch,action就會(huì)被nextReducer處理,處理的結(jié)果賦值給currentState。替換之后會(huì)執(zhí)行一遍初始化action。

combineReducers

代碼雖然看起來(lái)很長(zhǎng),但是大多都是用來(lái)處理校驗(yàn)reducer的。它接收的參數(shù)reducers是個(gè)對(duì)象,對(duì)象的value不能是undefined,必須是function。符合這個(gè)標(biāo)準(zhǔn)的reducer會(huì)被放入finalReducers中。
然后再對(duì)finalReducers進(jìn)行校驗(yàn),reducer必須有default處理,不能處理Redux內(nèi)部的action type,比如@@redux/INIT。然后返回一個(gè)函數(shù)combination(state = {}, action),它也是一個(gè)reducer,可以被再次和其他reducer combine。一般combination等同于currentReducer,它的返回結(jié)果會(huì)賦給state,combination的關(guān)鍵代碼如下:

var finalReducerKeys = Object.keys(finalReducers)
var hasChanged = false
var nextState = {}
for (var i = 0; i < finalReducerKeys.length; i++) {
    var key = finalReducerKeys[i]
    var reducer = finalReducers[key]
    var previousStateForKey = state[key]
    var nextStateForKey = reducer(previousStateForKey, action)
    nextState[key] = nextStateForKey
    hasChanged = hasChanged || nextStateForKey !== previousStateForKey
}
return hasChanged ? nextState : state

中間省略了reducer返回結(jié)果的校驗(yàn)。reducer就是通過(guò)這種方式從state中拿到對(duì)應(yīng)的state,然后把返回的數(shù)據(jù)組裝到state的對(duì)應(yīng)位置,很巧妙!

bindActionCreators

它接收兩個(gè)參數(shù)actionCreators和dispatch。如果actionCreators是函數(shù),就直接返回:

(...args) => dispatch(actionCreator(...args))

直接給這個(gè)返回的函數(shù)傳actionCreator的參數(shù)就可以直接觸發(fā)dispatch,這也是bindActionCreators的目的,簡(jiǎn)化操作,弱化dispatch的存在感。
如果actionCreators是個(gè)對(duì)象會(huì)進(jìn)行另外的操作,返回一個(gè)對(duì)象,下面是關(guān)鍵代碼。

var keys = Object.keys(actionCreators)
var boundActionCreators = {}
for (var i = 0; i < keys.length; i++) {
    var key = keys[i]
    var actionCreator = actionCreators[key]
    boundActionCreators[key] = (...args) => dispatch(actionCreator(...args))
}
return boundActionCreators

其實(shí)里面的處理和單個(gè)function的處理是一樣的。

applyMiddleware

一個(gè)可以被執(zhí)行三次的柯里化函數(shù),代碼雖然簡(jiǎn)單,但是給Redux帶來(lái)卻是無(wú)限可能。它的第一次執(zhí)行的時(shí)候參數(shù)是一系列的middlewares,第二次執(zhí)行參數(shù)是createStore,返回的是一個(gè)和createStore接收同樣參數(shù)的函數(shù),再次被執(zhí)行的話,就會(huì)返回dispatch強(qiáng)化之后的store。關(guān)鍵代碼如下:

var middlewareAPI = {
  getState: store.getState,
  dispatch: (action) => dispatch(action)
}
chain = middlewares.map(middleware => middleware(middlewareAPI))
const last = chain[funcs.length - 1]
const rest = chain.slice(0, -1)
dispatch = rest.reduceRight((composed, f) => f(composed), last(store.dispatch))

這里只寫了多個(gè)middleware的情況,單個(gè)middlewares更簡(jiǎn)單。代碼很簡(jiǎn)單,邏輯也很簡(jiǎn)單,作用很大。比如使用thunk可以做異步action。

以上基本是Redux的全部關(guān)鍵代碼,簡(jiǎn)單而強(qiáng)大。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

  • http://gaearon.github.io/redux/index.html ,文檔在 http://rac...
    jacobbubu閱讀 80,409評(píng)論 35 198
  • 一、什么情況需要redux? 1、用戶的使用方式復(fù)雜 2、不同身份的用戶有不同的使用方式(比如普通用戶和管...
    初晨的筆記閱讀 2,129評(píng)論 0 11
  • 學(xué)習(xí)必備要點(diǎn): 首先弄明白,Redux在使用React開(kāi)發(fā)應(yīng)用時(shí),起到什么作用——狀態(tài)集中管理 弄清楚Redux是...
    賀賀v5閱讀 9,066評(píng)論 10 58
  • 看到這篇文章build an image gallery using redux saga,覺(jué)得寫的不錯(cuò),長(zhǎng)短也適...
    smartphp閱讀 6,329評(píng)論 1 29
  • 前言 本文 有配套視頻,可以酌情觀看。 文中內(nèi)容因各人理解不同,可能會(huì)有所偏差,歡迎朋友們聯(lián)系我討論。 文中所有內(nèi)...
    珍此良辰閱讀 12,168評(píng)論 23 111

友情鏈接更多精彩內(nèi)容