學習Redux刷新了我的前端App狀態管理觀

寫在前面的話

聽過Redux好久了,不過真的開始學大概在一個月前,學會Redux之後我用一周多的時間重構了之前純React寫的一個小項目。學習的過程中心態變化很大,但是最終真的感覺如果再寫複雜一點的項目,我應該再也不會直接用state管理所有狀態了。Redux真的刷新了我的狀態管理觀。

本文的寫作目的在於讓那些一直在使用React,但是還沒有使用Redux管理複雜應用狀態的同學了解Redux的核心思想並能夠更加平滑的學習使用Redux。

為什麼要使用Redux

快雙十一了,突然想到一個比喻來解釋為什麼會有React這類框架的存在,為什麼我們要使用Redux,話說以前使用jQuery不也挺快樂的嘛。

一個比喻

想想現代物流系統發展的不同階段,我們寄一件東西的過程,

沒有物流系統時:

  • 打包準備好要送出去的東西;

  • 出門乘車到達朋友家,把東西送給朋友;

  • 很直接很方便,很費時間,送東西的距離有限(同城?);

出現了各大物流公司時:

  • 打包準備好要送出去的東西;

  • 出門到距離家最近的物流公司,填寫物品,收件人等基本信息;

  • 物流公司替你送物品到你的朋友處,自己可以回家做別的事情了;

  • 多了一個中介,要付一定的運送成本,但是東西可以送到遠在幾千公里之外的其它人了,用一點點錢節約了自己寶貴的時間,完成了以前我們不可能做的事情;

沒錯,沒有物流系統時對應的就是以前使用jQuery的前端階段,做一件事情很直接,如果只是送快遞給一個特別近的人,那當然是非常方便。就算你要從北京送一個東西到上海,在這個階段你也可以實現,但是需要付出的代價就太大了。

而出現了各大物流公司對應的就是使用React等框架的階段,學習React需要付出成本,如果你只需要做簡單的事情(比如說你想把東西送給你的鄰居),那使用此類框架其實顯得特別累贅,把簡單的事情複雜化了,但是如果你想做複雜一點的事情(同時寄禮物給三個不同城市的人),那麼選擇快遞公司是不會錯的。

如何理解React和Redux的關係?

我覺得我們可以把React比如為一家自建快遞系統的公司(京東?),在一定範圍內,它的快遞系統已經足夠好了(部分一二線城市一天內直達),但是要管理這個日益複雜的快遞系統,需要公司付出巨大的成本。

Redux可以看做業內最好的第三方快遞系統(順豐?),使用它比較貴(學習成本稍微有點高),但是他到達全國主要城市都會特別快,如果你是一個電商老闆,採用這個第三方物流系統之後,你只需要關注於自己的貨物,幾乎不用再去關心物流怎麼辦了。

不同於順豐每一次寄貨都要那麼高的價格,redux是一個學習一次,就可以免費寄貨的優秀第三方快遞系統,那我們當然要解散自己的物流公司來採用這個第三方的選擇的。不過話說回來,學習是需要有成本的,所以是直接使用React還是學習Redux再使用,這是你一個你需要依據你自己項目的實際情況作出選擇的事情。

不過這個例子可能並不足夠合適,Redux做的不僅僅是管理原來React里state裡面的狀態。Redux其實可以接管我們的app里所有的數據。接下來我們具體看看Redux究竟做了什麼。

Redux可以做什麼

我們常常聽到一種說法,「Redux是一個非常好的狀態管理器」,那究竟什麼是狀態呢。

重新理解前端中的狀態

想想我們平時看的網頁,app,或者任何其它和我們有交互的東西,我們感覺到交互的發生是因為界面依據我們的行為作出了反饋,界面所有的改變,其實都可以看作是狀態的改變,或者說界面會改變是因為我們的某個行為(事件)(`click`,`drag`,`move`...)觸發了某個函數,函數造成了狀態的改變,進而改變了界面。

如此看來,無論是顯示隱藏這種可見的狀態,還是從伺服器獲取更多的數據,這些都可以看做是狀態,而這些狀態就是我們的Redux要管理的。

換一個可能比較專業一點的說法吧,狀態包括

  • API State;(數據)

  • UI State;(UI的表現形式)

前端狀態管理史

為了更好的展示Redux的好,我們回顧一下前端的狀態管理史(稱為史其實並不合適,以下三種模式現在都有大量人在使用)。

中古

  • jQuery 時期

  • 我們使用諸如`$(element).addClass(active)`這樣的語句來改變狀態;

  • 對簡單的應用來說,這樣寫簡單明了,但是狀態一多一複雜就亂了,並不存在一個專門管理狀態的地方;

近代

  • React內部管理狀態時期

  • 我們引入了`state`來管理組件狀態,界面想顯示不同的樣子,我們通過各種函數來改變`state`來實現

  • 已經存在一個專門管理狀態的`state`(對象,數組)了,對大部分應用來說,用`state`來管理狀態已經足夠了,但是應用複雜了會使得狀態

現代

  • 引入Redux等狀態管理機制時期;

  • redux使用一個`store`來全局管理各種狀態,提供一些不算複雜的api來專門管理狀態;

  • 可以管理更加複雜的狀態,通過redux的管理,狀態的改變變得更加清晰,可預測,`redux`中的狀態是一個只讀屬性,通過一定的方法,可以回到已經經歷過的某個狀態(時間旅行);

(這裡沒有說到MVC等機制有興趣的同學可以看[這篇文章](Understanding Redux (or, How I Fell in Love with a JavaScript State Container))做進一步對比了解,基本觀點是MVC固然很好,但是配合React使用時,性價比不是那麼高了。)

Redux是怎麼管理狀態的

上面已經說了Redux管理狀態特別好,那Redux究竟是如何管理狀態的呢?狀態分為UI State 和 API State,Redux針對這兩部分也提供了兩種方法

  • 為改變已有的狀態提供了方案;

  • 為非同步獲取新的數據提供了方案;

Redux的數據流

還是用圖片來說明更加清楚

下圖說明了Redux和React的狀態流分別是怎麼樣的;

下圖說明了使用Redux管理狀態為什麼是可預測的

Redux的數據是如何流動的其實也是理解Redux的好處的關鍵部分之一,簡單來說每個事件會發送一個action,action通過dispatch觸發reduce,直接依據舊的state生成一個新state替代最頂層的store裡面原有的state。

有一篇文章以漫畫的形式把這個講的特別透[A Cartoon intro to redux](code-cartoons.com/a-car)

說了這麼多使用redux管理狀態的好處,但是你看到這裡可能依舊不知道如何使用`redux`,不要著急,我和你分享我的Redux學習經驗。

學習使用Redux

學習Redux前需要了解的基礎知識

如果你已經能很熟練的使用React,我覺得學習Redux需要了解的基礎知識,你應該都已經了解了。具體說來主要有以下內容;

  • React(Redux是flux架構的實現,雖然其也可以配合其它框架使用,但是它和React可能還是更配一些吧);

  • 基礎的ES6知識(Redux重視函數式編程,會使得編程的結構看起來更加簡潔);

  • 用了ES6(甚至ES7)當然免不了要學習使用Webpack,Babel等;

  • 還有一點,我覺得學習編程應該不怕折騰,使用Redux管理一個狀態可能需要改好幾個文件里的代碼才能實現,編程不再顯得那麼直接(比如 通過`connect.js`調用`action.js`里的某個`action`,並依據這個`action`觸發`reducer.js`裡面的某個`reducer`函數依據現有的`state`,創建一個新的`state`),redux把一些操作給抽象化了,如果思維沒有跟著改變,會讓人有一種redux文檔裡面的東西我都看懂了,但是我怎麼就是編不出來呢?那種痛苦的感覺。

學習使用Redux

網上關於Redux的教程特別多了([官方文檔](Read Me ?? Redux)寫的特別好),學習新概念是比較惱人的一個過程,所以我還是會對Redux提供的api做一個簡潔的描述,然後我會把我這段時間看過的我覺得比較好的文章的鏈接放在下面以供大家參考。

Redux中的一些概念

  • `state`:app中的狀態存放的地方,並且state是只讀的,不同於React,Redux中state的更改,其實是創建了一個全新的state;

  • `action`:是一個對象,作用和他的名字一樣,用來表明,你想要做的那件事情,該對象的屬性type,用來標記,你要做的事情;

  • `reducer`:是一個函數,接收當前state,和一個action作為參數,依據action基於當前的`state`生成新的`state`;

  • `dispatch`:推送某個`action`給`reducer`;

  • `action creater`:一個創建`action`的函數,返回一個`action`對象;

  • `非同步action`:返回一個函數,和中間件配合可以很容易的實現非同步操作;

  • `store`:可以理解為`state`的家,全局只有一個,有以下方法

  • `getState()`:獲取當前的state樹;

  • `dispatch(action)`:觸發一個action,創建state;

  • `subscribe(listener)`:

  • `replaceReducer(nextReducer)`

  • `combineRedecers(reducers)`:當我們的應用比較複雜的時候,我們可能會分開寫好幾個`reducer`,這個函數的作用就是把這些單獨的reduce合併為一個大的reduce,需要注意的是我們的state的結構和我們的各個`reducer`是一一對應的。

  • `applyMiddleware(...middlewares)`:告訴redux我們會用到那些中間件,比如說要用到基礎的非同步,我們會用到thunk中間件;

let store = createStore( comReducer, applyMiddleware(thunk));

  • `bindActionCreators(actionCreators, dispatch)`:綁定`actionCreator`和`dispatch`以供直接使用;

配合 React 使用Redux需要掌握的一些概念

redux只是管理狀態的一種方法,真的用在React里,使用作者提供的一個工具`react-redux`會更加方便,其api很簡單,主要有以下幾個;

  • `<Provider store>`,嵌套在React組件的最外層,因此可以把state傳給所有的組件(利用了React的`context`);

  • 把React組件分為容器組件和UI組件兩類,容器組件管理邏輯,UI組件管理顯示效果二者通過`connect`方法連接,容器組件一般由UI組件依據`connect`生成;

  • `mapStateToProps()`,存在於容器組件中,針對UI組件的各狀態(依據`state`,或者父組件的`props`)生成;

  • `mapDispatchToProps()`,存在於容器組件中,針對UI組件中的各可能改變state的事件定義的一系列的函數,依據`props`傳給UI組件;

Redux參考文獻

官方文檔

What the Flux?! Let』s Redux

a-cartoon-intro-to-redux

官網推薦的經典文章列表

阮一峰的三篇教程

Redux還有一個非常酷的工具,讓我們可以實時看到當前的state,使用redux不可不用啊。

酷酷的工具redux devtools

學會使用還是得靠實踐

在弄懂了一些關鍵的核心概念以後,如果還是不知道怎麼寫,就模仿官方給的多個例子針對自己的需求敲寫一次吧。模仿了兩個例子就肯定明白了。

後言

Redux也有自己的小生態,理解的一些技術輔助Redux更加方便的實現狀態管理,其中有一些是下一步我特別想了解的比如說Immutable.js,reselect,normalizr,當然因為學習了Redux,所以我也想對函數式編程做進一步理解,之前找到了一本非常好的關於函數式編程的書mostly-adequate-guide,分享給大家,大家一起學習。


推薦閱讀:

通過 JSX Control Statements 編寫 JSX
搜索延遲的簡單實現
Immutable 詳解及 React 中實踐
一篇文章講清楚React的基礎概念
React16 中的異常處理(前端監控實現)

TAG:React | 前端開發 |