Redux從設(shè)計(jì)到源碼

Redux背后的設(shè)計(jì)思想

在講設(shè)計(jì)思想前恋捆,先簡(jiǎn)單講下Redux是什么?我們?yōu)槭裁匆肦edux急鳄?

Redux是什么谤民?

Redux是JavaScript狀態(tài)容器,能提供可預(yù)測(cè)化的狀態(tài)管理疾宏。

它認(rèn)為:

  • Web應(yīng)用是一個(gè)狀態(tài)機(jī)张足,視圖與狀態(tài)是一一對(duì)應(yīng)的。
  • 所有的狀態(tài)坎藐,保存在一個(gè)對(duì)象里面为牍。

我們先來(lái)看看“狀態(tài)容器”、“視圖與狀態(tài)一一對(duì)應(yīng)”以及“一個(gè)對(duì)象”這三個(gè)概念的具體體現(xiàn)岩馍。

image.png

如上圖碉咆,Store是Redux中的狀態(tài)容器,它里面存儲(chǔ)著所有的狀態(tài)數(shù)據(jù)蛀恩,每個(gè)狀態(tài)都跟一個(gè)視圖一一對(duì)應(yīng)疫铜。

Redux也規(guī)定,一個(gè)State對(duì)應(yīng)一個(gè)View双谆。只要State相同壳咕,View就相同,知道了State顽馋,就知道View是什么樣谓厘,反之亦然。

比如寸谜,當(dāng)前頁(yè)面分三種狀態(tài):loading(加載中)竟稳、success(加載成功)或者error(加載失敗)程帕,那么這三個(gè)就分別唯一對(duì)應(yīng)著一種視圖住练。

現(xiàn)在我們對(duì)“狀態(tài)容器”以及“視圖與狀態(tài)一一對(duì)應(yīng)”有所了解了,那么Redux是怎么實(shí)現(xiàn)可預(yù)測(cè)化的呢愁拭?我們?cè)賮?lái)看下Redux的工作流程讲逛。

image.png

首先,我們看下幾個(gè)核心概念:

  • Store:保存數(shù)據(jù)的地方岭埠,你可以把它看成一個(gè)容器盏混,整個(gè)應(yīng)用只能有一個(gè)Store蔚鸥。
  • State:Store對(duì)象包含所有數(shù)據(jù),如果想得到某個(gè)時(shí)點(diǎn)的數(shù)據(jù)许赃,就要對(duì)Store生成快照止喷,這種時(shí)點(diǎn)的數(shù)據(jù)集合,就叫做State混聊。
  • Action:State的變化弹谁,會(huì)導(dǎo)致View的變化。但是句喜,用戶接觸不到State预愤,只能接觸到View。所以咳胃,State的變化必須是View導(dǎo)致的植康。Action就是View發(fā)出的通知,表示State應(yīng)該要發(fā)生變化了展懈。
  • Action Creator:View要發(fā)送多少種消息销睁,就會(huì)有多少種Action。如果都手寫存崖,會(huì)很麻煩冻记,所以我們定義一個(gè)函數(shù)來(lái)生成Action,這個(gè)函數(shù)就叫Action Creator来惧。
  • Reducer:Store收到Action以后檩赢,必須給出一個(gè)新的State,這樣View才會(huì)發(fā)生變化违寞。這種State的計(jì)算過(guò)程就叫做Reducer贞瞒。Reducer是一個(gè)函數(shù),它接受Action和當(dāng)前State作為參數(shù),返回一個(gè)新的State。
  • dispatch:是View發(fā)出Action的唯一方法返敬。

然后我們過(guò)下整個(gè)工作流程:

  1. 首先,用戶(通過(guò)View)發(fā)出Action乒融,發(fā)出方式就用到了dispatch方法。
  2. 然后摄悯,Store自動(dòng)調(diào)用Reducer赞季,并且傳入兩個(gè)參數(shù):當(dāng)前State和收到的Action,Reducer會(huì)返回新的State
  3. State一旦有變化奢驯,Store就會(huì)調(diào)用監(jiān)聽(tīng)函數(shù)申钩,來(lái)更新View。

到這兒為止瘪阁,一次用戶交互流程結(jié)束撒遣∮寿耍可以看到,在整個(gè)流程中數(shù)據(jù)都是單向流動(dòng)的义黎,這種方式保證了流程的清晰禾进。

為什么要用Redux?

前端復(fù)雜性的根本原因是大量無(wú)規(guī)律的交互和異步操作廉涕。

變化和異步操作的相同作用都是改變了當(dāng)前View的狀態(tài)泻云,但是它們的無(wú)規(guī)律性導(dǎo)致了前端的復(fù)雜,而且隨著代碼量越來(lái)越大狐蜕,我們要維護(hù)的狀態(tài)也越來(lái)越多壶愤。

我們很容易就對(duì)這些狀態(tài)何時(shí)發(fā)生、為什么發(fā)生以及怎么發(fā)生的失去控制馏鹤。那么怎樣才能讓這些狀態(tài)變化能被我們預(yù)先掌握,可以復(fù)制追蹤呢娇哆?

這就是Redux設(shè)計(jì)的動(dòng)機(jī)所在湃累。

Redux試圖讓每個(gè)State變化都是可預(yù)測(cè)的,將應(yīng)用中所有的動(dòng)作與狀態(tài)都統(tǒng)一管理碍讨,讓一切有據(jù)可循治力。

image.png

如上圖所示,如果我們的頁(yè)面比較復(fù)雜勃黍,又沒(méi)有用任何數(shù)據(jù)層框架的話宵统,就是圖片上這個(gè)樣子:交互上存在父子、子父覆获、兄弟組件間通信马澈,數(shù)據(jù)也存在跨層、反向的數(shù)據(jù)流弄息。

這樣的話痊班,我們維護(hù)起來(lái)就會(huì)特別困難,那么我們理想的應(yīng)用狀態(tài)是什么樣呢摹量?看下圖:

image.png

架構(gòu)層面上講涤伐,我們希望UI跟數(shù)據(jù)和邏輯分離,UI只負(fù)責(zé)渲染缨称,業(yè)務(wù)和邏輯交由其它部分處理凝果,從數(shù)據(jù)流向方面來(lái)說(shuō), 單向數(shù)據(jù)流確保了整個(gè)流程清晰。

我們之前的操作可以復(fù)制睦尽、追蹤出來(lái)器净,這也是Redux的主要設(shè)計(jì)思想。

綜上当凡,Redux可以做到:

  • 每個(gè)State變化可預(yù)測(cè)掌动。
  • 動(dòng)作與狀態(tài)統(tǒng)一管理四啰。

Redux思想追溯

Redux作者在Redux.js官方文檔Motivation一章的最后一段明確提到:

Following in the steps of Flux, CQRS, and Event Sourcing , Redux attempts to make state mutations predictable
by imposing certain restrictions on how and when updates can happen.

我們就先了解下Flux、CQRS粗恢、ES(Event Sourcing 事件溯源)這幾個(gè)概念柑晒。

什么是ES?

  • 不是保存對(duì)象的最新?tīng)顟B(tài)眷射,而是保存對(duì)象產(chǎn)生的事件匙赞。
  • 通過(guò)事件追溯得到對(duì)象最新?tīng)顟B(tài)。

舉個(gè)例子:我們平常記賬有兩種方式妖碉,直接記錄每次賬單的結(jié)果或者記錄每次的收入/支出涌庭,那么我們自己計(jì)算的話也可以得到結(jié)果,ES就是后者欧宜。

image.png

與傳統(tǒng)增刪改查關(guān)系式存儲(chǔ)的區(qū)別:

  • 傳統(tǒng)的增刪是以結(jié)果為導(dǎo)向的數(shù)據(jù)存儲(chǔ)坐榆,ES是以過(guò)程為導(dǎo)向存儲(chǔ)。
  • CRUD是直接對(duì)庫(kù)進(jìn)行操作冗茸。
  • ES是在庫(kù)里存了一系列事件的集合席镀,不直接對(duì)庫(kù)里記錄進(jìn)行更改。

優(yōu)點(diǎn):

  • 高性能:事件是不可更改的夏漱,存儲(chǔ)的時(shí)候并且只做插入操作豪诲,也可以設(shè)計(jì)成獨(dú)立、簡(jiǎn)單的對(duì)象挂绰。所以存儲(chǔ)事件的成本較低且效率較高屎篱,擴(kuò)展起來(lái)也非常方便。
  • 簡(jiǎn)化存儲(chǔ):事件用于描述系統(tǒng)內(nèi)發(fā)生的事情葵蒂,我們可以考慮用事件存儲(chǔ)代替復(fù)雜的關(guān)系存儲(chǔ)交播。
  • 溯源:正因?yàn)槭录遣豢筛牡模⑶矣涗浟怂邢到y(tǒng)內(nèi)發(fā)生的事情践付,我們能用它來(lái)跟蹤問(wèn)題堪侯、重現(xiàn)錯(cuò)誤,甚至做備份和還原荔仁。

缺點(diǎn):

  • 事件丟失:因?yàn)镋S存儲(chǔ)都是基于事件的伍宦,所以一旦事件丟失就很難保證數(shù)據(jù)的完整性。
  • 修改時(shí)必須兼容老結(jié)構(gòu):指的是因?yàn)槔系氖录豢勺兎α海援?dāng)業(yè)務(wù)變動(dòng)的時(shí)候新的事件必須兼容老結(jié)構(gòu)次洼。

CQRS(Command Query Responsibility Segregation)是什么?

顧名思義遇骑,“命令與查詢職責(zé)分離”-->”讀寫分離”卖毁。

image.png

整體的思想是把Query操作和Command操作分成兩塊獨(dú)立的庫(kù)來(lái)維護(hù),當(dāng)事件庫(kù)有更新時(shí),再來(lái)同步讀取數(shù)據(jù)庫(kù)亥啦。

看下Query端炭剪,只是對(duì)數(shù)據(jù)庫(kù)的簡(jiǎn)單讀操作。然后Command端翔脱,是對(duì)事件進(jìn)行簡(jiǎn)單的存儲(chǔ)奴拦,同時(shí)通知Query端進(jìn)行數(shù)據(jù)更新,這個(gè)地方就用到了ES届吁。

優(yōu)點(diǎn):

  • CQ兩端分離错妖,各自獨(dú)立。
  • 技術(shù)代碼和業(yè)務(wù)代碼完全分離疚沐。.

缺點(diǎn):

  • 強(qiáng)依賴高性能可靠的分布式消息隊(duì)列暂氯。

Flux是什么?

Flux是一種架構(gòu)思想亮蛔,下面過(guò)程中痴施,數(shù)據(jù)總是“單向流動(dòng)”,任何相鄰的部分都不會(huì)發(fā)生數(shù)據(jù)的“雙向流動(dòng)”究流,這保證了流程的清晰辣吃。Flux的最大特點(diǎn),就是數(shù)據(jù)的“單向流動(dòng)”梯嗽。

image.png
  1. 用戶訪問(wèn)View。
  2. View發(fā)出用戶的Action沽损。
  3. Dispatcher收到Action灯节,要求Store進(jìn)行相應(yīng)的更新。
  4. Store更新后绵估,發(fā)出一個(gè)“change”事件炎疆。

介紹完以上之后,我們來(lái)整體做一下對(duì)比国裳。

CQRS與Flux

相同:當(dāng)數(shù)據(jù)在write side發(fā)生更改時(shí)形入,一個(gè)更新事件會(huì)被推送到read side,通過(guò)綁定事件的回調(diào)缝左,read side得知數(shù)據(jù)已更新亿遂,可以選擇是否重新讀取數(shù)據(jù)。

差異:在CQRS中渺杉,write side和read side分屬于兩個(gè)不同的領(lǐng)域模式蛇数,各自的邏輯封裝和隔離在各自的Model中,而在Flux里是越,業(yè)務(wù)邏輯都統(tǒng)一封裝在Store中耳舅。

Redux與Flux

Redux是Flux思想的一種實(shí)現(xiàn),同時(shí)又在其基礎(chǔ)上做了改進(jìn)倚评。Redux還是秉承了Flux單向數(shù)據(jù)流浦徊、Store是唯一的數(shù)據(jù)源的思想馏予。

image.png

最大的區(qū)別:

  • Redux只有一個(gè)Store。

Flux中允許有多個(gè)Store盔性,但是Redux中只允許有一個(gè)霞丧,相較于Flux,一個(gè)Store更加清晰纯出,容易管理蚯妇。Flux里面會(huì)有多個(gè)Store存儲(chǔ)應(yīng)用數(shù)據(jù),并在Store里面執(zhí)行更新邏輯暂筝,當(dāng)Store變化的時(shí)候再通知controller-view更新自己的數(shù)據(jù)箩言;Redux將各個(gè)Store整合成一個(gè)完整的Store,并且可以根據(jù)這個(gè)Store推導(dǎo)出應(yīng)用完整的State焕襟。

同時(shí)Redux中更新的邏輯也不在Store中執(zhí)行而是放在Reducer中陨收。單一Store帶來(lái)的好處是,所有數(shù)據(jù)結(jié)果集中化鸵赖,操作時(shí)的便利务漩,只要把它傳給最外層組件,那么內(nèi)層組件就不需要維持State它褪,全部經(jīng)父級(jí)由props往下傳即可饵骨。子組件變得異常簡(jiǎn)單。

  • Redux中沒(méi)有Dispatcher的概念茫打。

Redux去除了這個(gè)Dispatcher居触,使用Store的Store.dispatch()方法來(lái)把a(bǔ)ction傳給Store,由于所有的action處理都會(huì)經(jīng)過(guò)這個(gè)Store.dispatch()方法老赤,Redux聰明地利用這一點(diǎn)轮洋,實(shí)現(xiàn)了與Koa、RubyRack類似的Middleware機(jī)制抬旺。Middleware可以讓你在dispatch action后弊予,到達(dá)Store前這一段攔截并插入代碼,可以任意操作action和Store开财。很容易實(shí)現(xiàn)靈活的日志打印汉柒、錯(cuò)誤收集、API請(qǐng)求责鳍、路由等操作竭翠。

除了以上,Redux相對(duì)Flux而言還有以下特性和優(yōu)點(diǎn):

  • 文檔清晰薇搁,編碼統(tǒng)一斋扰。
  • 逆天的DevTools,可以讓應(yīng)用像錄像機(jī)一樣反復(fù)錄制和重放。

目前传货,美團(tuán)外賣后端管理平臺(tái)的上單各個(gè)模塊已經(jīng)逐步替換為React+Redux開發(fā)模式屎鳍,流程的清晰為錯(cuò)誤追溯和代碼維護(hù)提供了便利,現(xiàn)實(shí)工作中也大大提高了人效问裕。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末逮壁,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子粮宛,更是在濱河造成了極大的恐慌窥淆,老刑警劉巖,帶你破解...
    沈念sama閱讀 217,277評(píng)論 6 503
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件巍杈,死亡現(xiàn)場(chǎng)離奇詭異忧饭,居然都是意外死亡,警方通過(guò)查閱死者的電腦和手機(jī)筷畦,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,689評(píng)論 3 393
  • 文/潘曉璐 我一進(jìn)店門词裤,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái),“玉大人鳖宾,你說(shuō)我怎么就攤上這事吼砂。” “怎么了鼎文?”我有些...
    開封第一講書人閱讀 163,624評(píng)論 0 353
  • 文/不壞的土叔 我叫張陵渔肩,是天一觀的道長(zhǎng)。 經(jīng)常有香客問(wèn)我拇惋,道長(zhǎng)周偎,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,356評(píng)論 1 293
  • 正文 為了忘掉前任蚤假,我火速辦了婚禮栏饮,結(jié)果婚禮上吧兔,老公的妹妹穿的比我還像新娘磷仰。我一直安慰自己,他們只是感情好境蔼,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,402評(píng)論 6 392
  • 文/花漫 我一把揭開白布灶平。 她就那樣靜靜地躺著,像睡著了一般箍土。 火紅的嫁衣襯著肌膚如雪逢享。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,292評(píng)論 1 301
  • 那天吴藻,我揣著相機(jī)與錄音瞒爬,去河邊找鬼。 笑死,一個(gè)胖子當(dāng)著我的面吹牛侧但,可吹牛的內(nèi)容都是我干的矢空。 我是一名探鬼主播,決...
    沈念sama閱讀 40,135評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼禀横,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼屁药!你這毒婦竟也來(lái)了?” 一聲冷哼從身側(cè)響起柏锄,我...
    開封第一講書人閱讀 38,992評(píng)論 0 275
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤酿箭,失蹤者是張志新(化名)和其女友劉穎,沒(méi)想到半個(gè)月后趾娃,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體缭嫡,經(jīng)...
    沈念sama閱讀 45,429評(píng)論 1 314
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,636評(píng)論 3 334
  • 正文 我和宋清朗相戀三年茫舶,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了械巡。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 39,785評(píng)論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡饶氏,死狀恐怖讥耗,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情疹启,我是刑警寧澤古程,帶...
    沈念sama閱讀 35,492評(píng)論 5 345
  • 正文 年R本政府宣布,位于F島的核電站喊崖,受9級(jí)特大地震影響挣磨,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜荤懂,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,092評(píng)論 3 328
  • 文/蒙蒙 一茁裙、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧节仿,春花似錦晤锥、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,723評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)。三九已至箭启,卻和暖如春壕翩,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背傅寡。 一陣腳步聲響...
    開封第一講書人閱讀 32,858評(píng)論 1 269
  • 我被黑心中介騙來(lái)泰國(guó)打工放妈, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留北救,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 47,891評(píng)論 2 370
  • 正文 我出身青樓芜抒,卻偏偏與公主長(zhǎng)得像扭倾,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子挽绩,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,713評(píng)論 2 354

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