前言
在學習了React之後, 緊跟著而來的就是Redux了~
在系統性的學習一個東西的時候, 瞭解其背景、設計以及解決了什麼問題都是非常必要的。 接下來記錄的是, 我個人在學習Redux時的一些雜七雜八~
Redux是什麼
通俗理解
介紹
先從官方的一句介紹看起:
Redux is a predictable state container for JavaScript apps. (Redux是Javascript應用程式的可預測狀態容器。)
當然,假如你在這之前並沒有接觸過相關的狀態管理庫或者框架, 看到這句話時是非常的懵逼的, 不過可以帶著這句話來一步步探索~
背景
隨著Javascript單頁面應用開發日趨複雜,JavaScript 需要管理比任何時候都要多的 state (狀態)。 這些 state 可能包括伺服器響應、快取資料、本地生成尚未持久化到伺服器的資料,也包括 UI 狀態,如啟用的路由,被選中的標籤,是否顯示載入動效或者分頁器等等。管理不斷變化的 state 非常困難。如果一個 model 的變化會引起另一個 model 變化,那麼當 view 變化時,就可能引起對應 model 以及另一個 model 的變化,依次地,可能會引起另一個 view 的變化。直至你搞不清楚到底發生了什麼。 -- Redux文件
上面這一大段引用概況起來就是一句話, state(狀態)在什麼時候什麼地方,因為什麼而變化成了一個不受控制的過程。(這不能忍,狀態如果無法預測以及控制)
那麼Redux就是試圖讓state的變化變得可預測。這些限制條件反映在 Redux 的三大原則中。
核心概念
1.Redux使用普通的物件來描述state,這個物件就是Modal。
2.要想更新 state 中的資料,你需要發起一個 action。Action 就是一個普通 JavaScript 物件用來描述發生了什麼。
3.為了把 action 和 state 串起來,開發一些函式,這就是 reducer。reducer 只是一個接收 state 和 action,並返回新的 state 的函式。
三大準則
- 只有一個state樹。
- state是隻讀的,只能通過action改變。
- reducer是純函式,沒有副作用。
瞭解到這些後,其實已經多少能明白Redux is a predictable state container for JavaScript apps. (Redux是Javascript應用程式的可預測狀態容器。)這句話,為什麼是可預測的? 因為只有一個state樹,並且它是隻讀的,而且只能通過action來改變(改變的過程變得清晰可追蹤),並且獲取state(狀態)只能通過reducer,而reducer是一個純函式(此處瞭解state是重點),沒有副作用,也就意味著我們能知道我們最終得到的state是什麼樣的。
api簡介
createStore(reducer, [preloadedState], [enhancer])
建立store的函式,返回一個物件, 包含getState\dispatch\subscribe\getReducer\replaceReducer等方法
combineReducers(reducers)
合併多個reducer
applyMiddleware(...middlewares) 中介軟體處理,在 實際的dispatch前呼叫一系列中介軟體, 類似於koa
bindActionCreators(actionCreators, dispatch)
繫結action和dispatch
compose(...functions)
函數語言程式設計中常見的方法, compose(funcA, funcB, funcC) => compose(funcA(funcB(funcC())))
React-redux
介紹
Redux官方提供的 React 繫結庫。 具有高效且靈活的特性。
動機
React是以元件化的形式開發。為了元件的複用以及程式碼的清晰,通常我們將元件分為容器元件以及UI元件。
關於容器元件和UI元件,推薦閱讀該文章,而引入了React-redux可以很好的幫助我們分離容器元件和UI元件。
為什麼選擇react-redux
- react-redux是官方提供的繫結庫,由redux開發者維護,可以很好的與redux保持同步。
- 它鼓勵元件分離。react-redux協助我們分離容器元件和UI元件,通過提供API連線store(提供資料)和UI元件,並且使得UI元件不需要知道存在Redux(複用)。
- 效能優化。雖然React速度很快,但是re-redering是非常消耗效能的,而react-redux的內部做了許多效能優化。
- 社群支援,因為是官方指定的繫結庫,所以擁有大量的使用者,社群活躍度高,問題也容易解決。
api簡介
使元件層級中的 connect() 方法都能夠獲得 Redux store。
store: 應用程式中唯一的 Redux store 物件
connect(mapStateToProps, mapDispatchToProps, mergeProps, options)
mapStateToProps(state, [ownProps]): stateProps: 對映state作為UI元件的props
mapDispatchToProps(dispatch, [ownProps]): dispatchProps: 對映dispatch作為UI元件的props
mergeProps(stateProps, dispatchProps, ownProps): props: 如果指定這個函式, 即合併mapStateToProps\mapDIspatchToProps\oweProps作為UI元件的props
options: 定製 connector 的行為
Redux存在的問題
與其說缺點,不如說是Redux的優勢而造成的不可避免的劣勢,問題應該辯證地看~
- 純淨。Redux只支援同步,讓狀態可預測,方便測試。 但不處理非同步、副作用的情況,而把這個丟給了其他中介軟體,諸如redux-thunk\redux-promise\redux-saga等等,選擇多也容易造成混亂~
- 囉嗦。那麼寫過Redux的人,都知道action\reducer\以及你的業務程式碼非常囉嗦,模板程式碼非常多。但是~,這也是為了讓資料的流動清晰明瞭。
- 效能。粗暴地、級聯式重新整理檢視(使用react-redux優化)。
- 分型。原生 Redux-react 沒有分形結構,中心化 store;
Redux的最佳實踐
vuex(dva)
事實上,如果用過vuex或者dva的話, 個人覺得還是會比較偏向於這種用法。比起Redux的囉嗦,dva幫忙簡化了很多步驟。具體的實現後續補充~
這裡先補充一點,vuex不是immutable,所以對於時間旅行這種業務不太友好。
Redux的實現淺析
前言
Redux的程式碼相對比較簡單,容易理解, 原始碼的解讀推薦看這篇文章, 本段主要是對程式碼裡一些個人覺得比較有意思的點進行分析~
createStore
在這裡看出,redux即使是在內部,也是函數語言程式設計~
當我們傳入了一個enhancer函式(即中介軟體),會把createStore本身當成引數傳給enhancer然後返回一個新的函式來呼叫 即 fn => fn
暴露出的subscribe函式也是挺有意思的, 首先是isSubscribed這個變數, 其實就是一種非常基礎的閉包使用,
然後是每次訂閱或者取消訂閱的時候,都會在dispatch之前儲存一次快照, 然後當前的dispatch用的是上一份快照,而下一個dispatch則是使用當前這一份的快照
compose
非常簡潔的寫出了函數語言程式設計的一個常用函式(...args) => f(g(h(...args))).
combineReducer
可以看出,每一次action都會重新計算所有的reducer~ 但如果不是非常巨大的state樹,並且拆分了很多模組,個人認為其實影響不大
bindActionCreator和applyMiddleware相對容易理解, 這裡就不贅述啦