ReactJS &Flux &Redux 的設計思想與關係

GeekQiaQia發表於2019-04-12

前言:

在WEB1.0時代,資料改變與頁面重新整理的機制比較簡單粗暴:“後端改變state,前端整個頁面view重新整理”;

web2.0時代,我們希望:

“改變state,view自動更新”

虛擬DOM

瀏覽器裡的DOM tree克隆一份完整的映象到記憶體,也就是所謂的“virtual DOM”,

當頁面的state發生變化以後,根據最新的state重新生成一份virtual DOM(相當於在記憶體裡“重新整理”整個頁面),將它和之前的virtual DOM做比對(diff),然後在瀏覽器裡只渲染被改變的那部分內容,這樣瀏覽器的效能損耗和使用者體驗不就都不成問題了嗎?

**在絕大多數的WEB開發中:**js引擎的效能和記憶體完全沒有被充分利用,我們正好可以火力全開,利用js的這部分效能紅利,實現記憶體中virtual DOM的diff工作,完美!

ReactJS:

  • react非常具有表達力的jsx語法和完善的模組化結構,

  • view的元件化和模組化非常有利於分工協作、程式碼的積累複用以及單元測試,

伴隨著reactJS 前端框架的崛起,redux這些專注於管理state的輕量級框架橫空出世;

*由於React的“state-view”模式可以讓開發者的大腦得到一種“單向流”的舒適體驗。那為什麼單向流的思維狀態更加舒適呢?

這是因為在單向流狀態下,要解決的問題如同一個函式對映,已知什麼(比如state)是固定不變的,要得到什麼(比如view)是定義明確,而人的思維非常習慣於這種定義明確的、沒有“分叉”和“環路”的函式式問題。

頁面呈現的state可以通過模組屬性(props)從父模組傳遞到子模組。這種"樹狀"分流機制,有點像植物將養分(state)從根部不斷運輸到細枝末葉的過程*

Flux思想:

  • flux 與 react 是完全獨立的概念,咩有直接的關係;
  • flux 不是JS庫,而是一種前端程式碼的組織思想;=>redux 庫可以認為是flux 思想的實現;
  • flux 的核心思想和程式碼實現雖然很簡單,但是在model(view)-view 開發模式中,起著非常重要的作用;
  • mvc設計思想: MVC開發模式, 主要講的是在開發互動應用時,怎樣將不同功能的程式碼拆分到不同檔案或區塊,以便降低程式碼的耦合度,提高程式碼的可讀性和健壯性。 簡單理解就是:要將 Model-View-Controller 這三部分程式碼拆分到不同檔案。當開發小型的web應用的時候,mvc可以應付;但是當中大型應用的時候,多model,多view的時候,model-view的‘單向流’被破壞帶來的混亂,帶來難以接受的局面;這種設計模式的model-view關係;facebook團隊總結說:MVC模式難以scale up

ReactJS &Flux &Redux 的設計思想與關係

flux設計模式就是解決以上model-view混亂=>

flux 設計思想的誕生

現在我們又可以從伺服器端的MVC模式中獲得靈感了!

因為我們注意到,伺服器端的controller通常也需要對很多Model產生修改,但在程式碼結構中卻集中在一起,沒有散落一地。原因很簡單:

  • 由於server和client是遠端通訊的關係,因此為了儘量減少通訊耦合,client每個操作的全部資訊都以http請求的形式被概括成了精簡的“作用量”(action)。
  • 請求的url路徑約定了使用者的操作意圖(當然RESTful概念中,請求的method也可以反映操作意圖),request參數列徵了該“意圖”的具體內容。正是基於這個action的抽象,client端的互動操作才可以被集中轉移到server端的controller中做統一響應。

對比之下,我們立刻發現上述程式碼片斷中前端MVC模式的“痛點”所在:不是MVC模式錯了,而是我們壓根缺少了一個和使用者互動行為有關的action抽象!因此,對model的具體操作才沒法從各個view元件中被剝離出來,放到一處。

參考http請求,我們將要定義的action,需要一個typeName用來表示對model操作的意圖(類似於http請求的url路徑),還可能需要其他欄位,用來描述怎樣具體操作model(類似於http請求的引數)。

也就是說,當使用者在view上的互動行為(例如點選提交按鈕)應當引起Model發生變化時,我們不直接修改model,而是簡單地dispatch一個action(其實跟常見的event機制沒有什麼區別)以表達修改model的意圖,這些action將被集中轉移給資料端(models),然後資料端會根據這些action做出需要的自我更新。同時,我們考慮到react中view元件的樹狀分流結構,所以有如下圖所示:

ReactJS &Flux &Redux 的設計思想與關係
圖中A表示Action,V表示View元件,Models部分的結構會進一步討論。

稍微總結一下:從程式碼層面而言,flux無非就是一個常見的event dispatcher,其目的是要將以往MVC中各個View元件內的controller程式碼片斷提取出來放到更加恰當的地方進行集中化管理,並從開發體驗上實現了舒適清爽、容易駕馭的“單向流”模式。 所以我覺得,Flux與其說是對前端MVC模式的顛覆,倒不如說是對前端MVC思想的補充和優化。

但為了區分於以往的MVC模式,並向facebook的貢獻表達敬意,後面我們將把這種優化後的 Model-View-Controller 開發模式在React背景下正式稱為Flux模式

ReactJS &Flux &Redux 的設計思想與關係

問題:

React的可以通過View Component把頁面呈現進行“原子化”拆分(即上圖中蘭色區域的樹狀分流結構);

Flux打通了State-View的任督二脈(綠色區域),並通過action抽象把使用者互動行為
進行了“原子化”拆分;

複製程式碼

那麼聯絡上面的圖示,我們自然要問資料端(紫色區域)的處理,可否同樣被“原子化”拆分?

redux登場 (資料端的“原子化”)

redux 中的reduce機制,將state端的資料處理進行‘原子化’拆分。redux是來自函數語言程式設計(Functional Programming)的一朵奇葩,據說很有背景([參考連結](Prior Art | Redux) ) reducer,從程式碼上說,其實就是一個函式,具有如下形式:

(previousState, action) => newState
複製程式碼

reducer作為一個函式,可以根據web應用之前的狀態(previousState)和互動行為(通過flux中提到的action來表徵),決定web應用的下一狀態(newState),從而實現state端的資料更新處理。這個函式行為和大名鼎鼎的“Map-Reduce”概念中的Reduce操作非常類似,因而稱這個函式為“Reducer”。

"shut up and show me the code" redux.js.org/basics/exam…

這裡不打算詳細講解Redux的具體使用,而只想通過一個Redux對state資料進行操作的程式碼片斷,管窺一下reducer機制對資料進行拆分和組裝的簡潔過程。程式碼片斷如下

ReactJS &Flux &Redux 的設計思想與關係
其中的todos是和任務列表資料相關的reducer,todo是和單條任務資料有關的reducer。注意:在todos的函式體內呼叫了todo,並將action作為引數原樣傳遞給了todo,這種乾淨利落地通過函式呼叫將action由 “parent reducer” 傳遞給 “child reducer”,是redux實現資料處理拆分的普遍方式。

回味一下,我們應該可以體會到,這種資料處理“原子化”拆分的方式和react中view元件的拆分有異曲同工之妙,二者都會形成一種“樹狀”分流結構(在react的view hierarchy中,資料通過props的直接賦值實現單向流;在redux的reducer hierarchy中,資料通過action的函式傳參實現單向流)。

visibilityFilter是和列表顯示狀態相關的另一個reducer;combineReducers將visibilityFilter和todos合併為整個應用的reducer,也就是todoApp。這個過程,從感覺上也和react中view元件的合併過程非常相像。

createStore是一個工廠函式。通過它,todoApp(相當於一個資料處理的引擎)被裝配到整個應用的state容器,也就是store中。可以通過store的getState方法獲取整個應用的state;同時,store也是一個event dispatcher,可以通過其dispatch和subscribe方法,分別實現觸發action事件和註冊對action事件的響應函式。總言之,從概念上來說 Redux = Reducer + Flux

總結:

全體亮相

現在React開發模式中的幾個核心概念已經全部出場亮相。我們俯瞰一下整個開發流程:首先,react框架為我們理順了 store --> view 的“單向”工作流(store是state的容器);然後,redux框架為我們理順了 view --> store 的**“單向”**工作流。並且,react和redux都以元件化的形式可以將各自負責的功能進行靈活地組裝或拆分,最大程度上確保我們“一次只需要專注於一個區域性問題”。具體來說,分為以下步驟:

1.單例store的資料在react中可以通過view元件的屬性(props)不斷由父模組**“單向”**傳遞給子模組,形成一個樹狀分流結構。如果我們把redux比作整個應用的“心肺” (redux的flux功能像心臟,reducer功能像肺部毛細血管),那麼這個過程可以比作心臟(store)將氧分子(資料)通過動脈毛細血管(props)送到各個器官組織(view元件)

2.末端的view元件,又可以通過flux機制,將攜帶互動意圖資訊的action反饋給store。這個過程有點像將攜帶代謝產物的“紅細胞”(action)通過靜脈毛細血管又泵迴心髒(store)

3.action流回到store以後,action以引數的形式又被分流到各個具體的reducer元件中,這些reducer同樣構成一個樹狀的hierarchy。這個過程像靜脈血中的紅細胞(action)被運輸到肺部毛細血管(reducer元件)

4.接收到action後,各個child reducer以返回值的形式,將最新的state返回給parent reducer,最終確保整個單例store的所有資料是最新的。這個過程可以比作肺部毛細血管的血液充氧後,又被重新泵回了心臟

5.回到步驟1

用圖示的方式表達:

ReactJS &Flux &Redux 的設計思想與關係

  • 圖中A表示Action,V表示View元件,R表示Reducer。

  • 為了確保我們比較容易理解程式的全域性行為,或者說提高程式行為的確定性(predictable),我們一般期望具有類似職能的程式碼片斷被“平鋪”著擺放在一。

  • 因此圖示中相同顏色區域的程式碼通常會被放到同一個資料夾/檔案中。

  • 另外,同樣出於提高程式的確定性,redux所遵循的函數語言程式設計鼓勵我們使用pure function和immutable。

參考: www.cnblogs.com/dreamingbao…

相關文章