Redux專題:考古
本文是『horseshoe·Redux專題』系列文章之一,後續會有更多專題推出來我的 GitHub repo 閱讀完整的專題文章來我的 個人部落格 獲得無與倫比的閱讀體驗
React的橫空出世給前端帶來一場革命。其實隨React一同開源的還有一個叫Flux的東西。Flux是管理應用狀態的一種架構。光憑一個props是無法實現血緣關係疏遠的元件之間的狀態同步的。雖然context加回調可以勉強做到這一點,但是Flux架構有其更加深遠的意義。
你看,從一開始facebook的工程師就知道只憑React無法支撐大型的應用開發。但是為什麼稱Flux為一種架構而不是一個類庫或者框架呢?因為它的出現主要是為了提出一種思想,而不是作為一個真正成熟的類庫。facebook的工程師希望社群根據這種思想湧現出多樣化的實現方式。
那麼,Flux提出的思想是什麼呢?
這一切還要從MVC架構說起。
MVC
以前端舉例,MVC架構將一個應用分為Model
、View
和 Controller
三部分。Model
代表資料模型,用來存放業務邏輯;View
代表檢視,是最終的介面效果;Controller
代表控制器,響應使用者的操作。
一個使用者操作View
發出指令,Controller
處理該指令並且將處理結果傳遞給Model
,Model
再將更新後的資料渲染到View
上。
當一個應用越來越複雜時,如何去維護龐大的程式碼和讓新人快速的上手就變的緊迫起來。MVC架構的分層思想就是服務於這一目的,它並不能讓應用的效能提升,但是它可以讓程式碼對開發者更友好。不過影響程式碼對開發者友好度的可不止這一點,更重要的是資料的流向在開發者面前的清晰度。
MVC架構有沒有對資料的流向做很好的控制呢?並沒有。它有一套理想中的流程,就是上面講到的檢視更新的操作過程,然而View
可不可以直接給Model
發指令?當然可以,並且很多開發者就是這麼做的。如果每一層都可以和每一層通訊,程式碼的可讀性將變的混亂不堪。
Flux
Flux就是為解決這個問題而生的。MVC架構的問題在於沒有嚴格控制資料的流向,所以facebook的工程師將Flux設計成資料只能走單通道,一個特定的操作才能觸發另一個特定的操作。
一個Flux應用包含四部分:
-
Action
,一個動作物件,相當於使用者的指令。 -
Dispatcher
,處理動作的派發,相當於MVC架構的Controller
-
Store
,負責儲存資料,相當於MVC架構的Model
。 -
View
,和MVC架構一樣。
使用者觸發View
上的事件傳送Action
,Action
只能通過Dispatcher.dispatch
方法傳送出去,Store
更新自身的資料然後View
根據Store
重新渲染。一環扣一環,只能按照這個流程走下去。
這就是我們說的單向資料流。
至此,我們實現了功能的分層和資料的單向流動,程式碼不再那麼混亂了。
Redux
我們的主角是Redux,它也是目前React社群最受歡迎的狀態管理框架。實際上,Redux就是Flux的門徒之一。它如此受歡迎,一定是因為它在Flux的基礎上做對了什麼事情。
Flux解決了單向資料流的問題,那麼Redux解決了什麼問題呢?
我認為Redux的貢獻主要是這兩點:
- 推崇唯一資料來源。
- 加入函數語言程式設計思想。
狀態只讀
和只能通過純函式來改變狀態
都是函數語言程式設計的特性,目的就是為了改變狀態的過程中不產生副作用。
咱們先來說說唯一資料來源是怎麼回事?
Flux的設計是將每一塊相對獨立的狀態分別用一個Store來儲存。這樣的好處是顯而易見的,每一個Store的體積都足夠小,物件的巢狀不會很深。
壞處是什麼呢?
Dispatcher在派發動作的時候,會依次訪問每一個Store。這樣雖然會損失一些效能,但是Dispatcher的邏輯可以做到極簡,它不用知道這個動作應該派發給誰,都給它們發一遍得了。假如Store A對Store B存在依賴關係,那麼它們的狀態更新順序就很重要,而Dispatcher哪管這些個兒女情長。於是開發者需要通過Dispatcher註冊回撥函式返回的token來手動管理Store之間的依賴關係。
這無疑又是對開發者不友好的設計。
所以Redux乾脆,只維護一個全域性的Store,讓你開發者再嗶嗶嗶。正事不幹,就知道嗶嗶嗶。
需要注意的是,Redux並沒有從機制上阻止開發者使用多個Store管理應用,開發者還是可以作妖的。不過這又回到Flux的老路上去了,對開發者一點好處都沒有。Redux選擇用思想和約定來約束開發者。
再來說函數語言程式設計。
函數語言程式設計思想的提出最早是為了處理複雜的計算任務。計算任務的要求是什麼呢?結果可預期,任務之間不會相互影響。
相同的輸入,總是有相同的輸出。對應到函式,就是不能修改已有的資料,只要修改已有的資料,理論上就有可能輸出不同的結果。那麼我確實有修改資料的需求怎麼辦?生成一個新的衍生資料。
執行某個任務不會對外部產生影響,也就是所謂的沒有副作用。
我們想一下,Redux加入函數語言程式設計思想的目的不還是為了讓單項資料流更加清晰和單純麼?
Redux給前端帶來的另一個重大成果是時間旅行式的除錯。開發者可以回到任意時間點的案發現場,看看那個臭蟲到底是哪個陰溝裡生出來的。
如果你看完本專題,就會發現Redux的作者Dan Abramov從一開始設計Redux就是奔著時間旅行式的除錯去的。時間旅行才是Redux寫法這麼繁瑣的罪魁禍首。
所以Flux的歷史功績是為前端引入了一種思想,而Redux僅僅是改進這種思想而已,還算不上一代宗師。不過Redux本來就是Flux的門徒之一,作為門徒能夠將師門發揚光大也是一代豪傑了。
Redux專題一覽