Redux源碼剖析

前面寫了《React組件性能優(yōu)化》《Redux性能優(yōu)化》《React-Redux性能優(yōu)化》官疲,但是都沒有從這些框架的實現(xiàn)上講為什么肥哎?這次就從源碼上來分析一下這些框架的實現(xiàn)原理客冈,以更深入的理解這些框架爱葵,并更好的使用它們河劝。

Redux的api很簡單惠啄,下面一個一個的分析慎恒。
createStore


首先說下它的三個參數(shù)reducer、preloadedState撵渡、enhancer融柬。reducer是唯一必傳的參數(shù),它很重要趋距,因為它決定了整個state粒氧。preloadedState就是state的初始值。第三個參數(shù)不是特別常用节腐,它是個函數(shù)外盯,如果它存在的情況下,會執(zhí)行下面的語句:

enhancer(createStore)(reducer, preloadedState)

很顯然enhancer和middleware作用很像翼雀,用來增強(qiáng)store饱苟。

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

currentReducer = reducer
currentState = preloadedState

這兩個變量很重要狼渊,因為很多操作都和它們相關(guān)箱熬。

subscribe

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

nextListeners = currentListeners.slice()

保證nextListeners的修改不會影響currentListeners。

subscribe(listener)的返回值是個函數(shù)驱入,執(zhí)行這個函數(shù)就會unsubscribe(listener)赤炒,取消監(jiān)聽。

dispatch

很重要亏较,因為只能通過它修改state莺褒,它只接收一個參數(shù)action,action必須是簡單對象雪情,而且必須有type屬性遵岩。在dispatch內(nèi)部關(guān)鍵代碼是:

currentState = currentReducer(currentState, action)

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

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

后面3行代碼是用來調(diào)用subscribe傳進(jìn)來的listeners表悬,按順序執(zhí)行它們,沒有任何條件判斷丧靡,也就是說只要執(zhí)行dispatch蟆沫,所有的listeners都會執(zhí)行,不管state有沒有發(fā)生改變温治,而且listeners執(zhí)行的時候是沒參數(shù)的饭庞。

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

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

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

dispatch({ type: ActionTypes.INIT })

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

getState

獲取store中的state本今,很簡單,主要代碼:

return currentState

在沒有dispatch正在執(zhí)行的情況下主巍,直接返回前面說很重要的currentState冠息。

replaceReducer

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

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

直接拿nextReducer替換掉前面說很重要的currentReducer孕索,后面再執(zhí)行dispatch逛艰,action就會被nextReducer處理,處理的結(jié)果賦值給currentState搞旭。替換之后會執(zhí)行一遍初始化action散怖。

combineReducers

代碼雖然看起來很長,但是大多都是用來處理校驗reducer的肄渗。它接收的參數(shù)reducers是個對象镇眷,對象的value不能是undefined,必須是function翎嫡。符合這個標(biāo)準(zhǔn)的reducer會被放入finalReducers中欠动。
然后再對finalReducers進(jìn)行校驗,reducer必須有default處理惑申,不能處理Redux內(nèi)部的action type具伍,比如@@redux/INIT翅雏。然后返回一個函數(shù)combination(state = {}, action),它也是一個reducer沿猜,可以被再次和其他reducer combine枚荣。一般combination等同于currentReducer,它的返回結(jié)果會賦給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é)果的校驗橄妆。reducer就是通過這種方式從state中拿到對應(yīng)的state,然后把返回的數(shù)據(jù)組裝到state的對應(yīng)位置祈坠,很巧妙害碾!

bindActionCreators

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

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

直接給這個返回的函數(shù)傳actionCreator的參數(shù)就可以直接觸發(fā)dispatch慌随,這也是bindActionCreators的目的,簡化操作躺同,弱化dispatch的存在感阁猜。
如果actionCreators是個對象會進(jìn)行另外的操作,返回一個對象蹋艺,下面是關(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

其實里面的處理和單個function的處理是一樣的。

applyMiddleware

一個可以被執(zhí)行三次的柯里化函數(shù)捎谨,代碼雖然簡單民效,但是給Redux帶來卻是無限可能。它的第一次執(zhí)行的時候參數(shù)是一系列的middlewares涛救,第二次執(zhí)行參數(shù)是createStore畏邢,返回的是一個和createStore接收同樣參數(shù)的函數(shù),再次被執(zhí)行的話检吆,就會返回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))

這里只寫了多個middleware的情況,單個middlewares更簡單咧栗。代碼很簡單逆甜,邏輯也很簡單,作用很大致板。比如使用thunk可以做異步action交煞。

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

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末素征,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌御毅,老刑警劉巖根欧,帶你破解...
    沈念sama閱讀 218,858評論 6 508
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異端蛆,居然都是意外死亡凤粗,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,372評論 3 395
  • 文/潘曉璐 我一進(jìn)店門今豆,熙熙樓的掌柜王于貴愁眉苦臉地迎上來嫌拣,“玉大人,你說我怎么就攤上這事呆躲∫熘穑” “怎么了?”我有些...
    開封第一講書人閱讀 165,282評論 0 356
  • 文/不壞的土叔 我叫張陵插掂,是天一觀的道長灰瞻。 經(jīng)常有香客問我,道長辅甥,這世上最難降的妖魔是什么酝润? 我笑而不...
    開封第一講書人閱讀 58,842評論 1 295
  • 正文 為了忘掉前任,我火速辦了婚禮璃弄,結(jié)果婚禮上袍祖,老公的妹妹穿的比我還像新娘。我一直安慰自己谢揪,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,857評論 6 392
  • 文/花漫 我一把揭開白布捐凭。 她就那樣靜靜地躺著拨扶,像睡著了一般。 火紅的嫁衣襯著肌膚如雪茁肠。 梳的紋絲不亂的頭發(fā)上患民,一...
    開封第一講書人閱讀 51,679評論 1 305
  • 那天,我揣著相機(jī)與錄音垦梆,去河邊找鬼匹颤。 笑死,一個胖子當(dāng)著我的面吹牛托猩,可吹牛的內(nèi)容都是我干的印蓖。 我是一名探鬼主播,決...
    沈念sama閱讀 40,406評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼京腥,長吁一口氣:“原來是場噩夢啊……” “哼赦肃!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 39,311評論 0 276
  • 序言:老撾萬榮一對情侶失蹤他宛,失蹤者是張志新(化名)和其女友劉穎船侧,沒想到半個月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體厅各,經(jīng)...
    沈念sama閱讀 45,767評論 1 315
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡镜撩,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,945評論 3 336
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了队塘。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片袁梗。...
    茶點(diǎn)故事閱讀 40,090評論 1 350
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖人灼,靈堂內(nèi)的尸體忽然破棺而出围段,到底是詐尸還是另有隱情,我是刑警寧澤投放,帶...
    沈念sama閱讀 35,785評論 5 346
  • 正文 年R本政府宣布奈泪,位于F島的核電站,受9級特大地震影響灸芳,放射性物質(zhì)發(fā)生泄漏涝桅。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,420評論 3 331
  • 文/蒙蒙 一烙样、第九天 我趴在偏房一處隱蔽的房頂上張望冯遂。 院中可真熱鬧,春花似錦谒获、人聲如沸蛤肌。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,988評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽裸准。三九已至,卻和暖如春赔硫,著一層夾襖步出監(jiān)牢的瞬間炒俱,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 33,101評論 1 271
  • 我被黑心中介騙來泰國打工爪膊, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留权悟,地道東北人。 一個月前我還...
    沈念sama閱讀 48,298評論 3 372
  • 正文 我出身青樓推盛,卻偏偏與公主長得像峦阁,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子小槐,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,033評論 2 355

推薦閱讀更多精彩內(nèi)容

  • http://gaearon.github.io/redux/index.html 拇派,文檔在 http://rac...
    jacobbubu閱讀 79,964評論 35 198
  • 一荷辕、什么情況需要redux? 1件豌、用戶的使用方式復(fù)雜 2疮方、不同身份的用戶有不同的使用方式(比如普通用戶和管...
    初晨的筆記閱讀 2,030評論 0 11
  • 學(xué)習(xí)必備要點(diǎn): 首先弄明白,Redux在使用React開發(fā)應(yīng)用時茧彤,起到什么作用——狀態(tài)集中管理 弄清楚Redux是...
    賀賀v5閱讀 8,902評論 10 58
  • 看到這篇文章build an image gallery using redux saga骡显,覺得寫的不錯,長短也適...
    smartphp閱讀 6,158評論 1 29
  • 前言 本文 有配套視頻曾掂,可以酌情觀看惫谤。 文中內(nèi)容因各人理解不同,可能會有所偏差珠洗,歡迎朋友們聯(lián)系我討論溜歪。 文中所有內(nèi)...
    珍此良辰閱讀 11,906評論 23 111