深入學習和理解 Redux
本文首發於 vivo網際網路技術 微信公眾號
連結: https://mp.weixin.qq.com/s/jhgQXKp4srsl9_VYMTZXjQ
作者:曾超
Redux官網上是這樣描述Redux,Redux is a predictable state container for JavaScript apps.(Redux是JavaScript狀態容器,提供可預測性的狀態管理)。 目前Redux GitHub有5w多star,足以說明 Redux 受歡迎的程度。
一、Why Redux
在說為什麼用 Redux 之前,讓我們先聊聊元件通訊有哪些方式。常見的元件通訊方式有以下幾種:
-
父子元件:props、state/callback回撥來進行通訊
-
單頁面應用:路由傳值
-
全域性事件比如EventEmitter監聽回撥傳值
-
react中跨層級元件資料傳遞Context(上下文)
在小型、不太複雜的應用中,一般用以上幾種元件通訊方式基本就足夠了。
但隨著應用逐漸複雜,資料狀態過多(比如服務端響應資料、瀏覽器快取資料、UI狀態值等)以及狀態可能會經常發生變化的情況下,使用以上元件通訊方式會很複雜、繁瑣以及很難定位、除錯相關問題。
因此狀態管理框架(如 Vuex、MobX、Redux等)就顯得十分必要了,而 Redux 就是其中使用最廣、生態最完善的。
二、Redux Data flow
在一個使用了 Redux 的 App應用裡面會遵循下面四步:
第一步:透過store.dispatch(action)來觸發一個action,action就是一個描述將要發生什麼的物件。如下:
第二步:Redux會呼叫你提供的 Reducer函式。
第三步:根 Reducer 會將多個不同的 Reducer 函式合併到單獨的狀態樹中。
第四步:Redux store會儲存從根 Reducer 函式返回的完整狀態樹。
所謂一圖勝千言,下面我們結合 Redux 的資料流圖來熟悉這一過程。
三、Three Principles(三大原則)
1、 Single source of truth:單一資料來源,整個應用的state被儲存在一個物件樹中,並且只存在於唯一一個store中。
2、 State is read-only:state裡面的狀態是隻讀的,不能直接去修改state,只能透過觸發action來返回一個新的state。
3、 Changes are made with pure functions:要使用純函式來修改state。
四、Redux原始碼解析
Redux 原始碼目前有js和ts版本,本文先介紹 js 版本的 Redux 原始碼。Redux 原始碼行數不多,所以對於想提高原始碼閱讀能力的開發者來說,很值得前期來學習。
Redux原始碼主要分為6個核心js檔案和3個工具js檔案,核心js檔案分別為index.js、createStore.js、compose.js、combineRuducers.js、bindActionCreators.js和applyMiddleware.js檔案。
接下來我們來一一學習。
1、index.js
index.js是入口檔案,提供核心的API,如createStore、combineReducers、applyMiddleware等。
export { createStore, combineReducers, bindActionCreators, applyMiddleware, compose, __DO_NOT_USE__ActionTypes }
2、createStore.js
createStore是 Redux 提供的API,用來生成唯一的store。store提供getState、dispatch、subscibe等方法,Redux 中的store只能透過dispatch一個action,透過action來找對應的 Reducer函式來改變。
export default function createStore(reducer, preloadedState, enhancer) {... }從原始碼中可以知道,createStore接收三個引數:Reducer、preloadedState、enhancer。
Reducer是action對應的一個可以修改store中state的純函式。
preloadedState代表之前state的初始化狀態。
enhancer是中介軟體透過applyMiddleware生成的一個加強函式。store中的getState方法是獲取當前應用中store中的狀態樹。
/** * Reads the state tree managed by the store. * * @returns {any} The current state tree of your application. */function getState() { if (isDispatching) { throw new Error( 'You may not call store.getState() while the reducer is executing. ' + 'The reducer has already received the state as an argument. ' + 'Pass it down from the top reducer instead of reading it from the store.' ) } return currentState }
dispatch方法是用來分發一個action的,這是唯一的一種能觸發狀態發生改變的方法。subscribe是一個監聽器,當一個action被dispatch的時候或者某個狀態發生改變的時候會被呼叫。
3、combineReducers.js
4、bindActionCreators.js
bindActionCreator就是將傳送actions的過程簡化,當呼叫這個返回的函式時就自動呼叫dispatch,傳送對應的action。
bindActionCreators根據不同型別的actionCreators做不同的處理,actionCreators是函式就返回函式,是物件就返回一個物件。主要是將actions轉化為dispatch(action)格式,方便進行actions的分離,並且使程式碼更加簡潔。
5、compose.js
compose是函式式變成裡面非常重要的一個概念,在介紹compose之前,先來認識下什麼是 Reduce?官方文件這麼定義reduce:reduce()方法對累加器和陣列中的每個元素(從左到右)應用到一個函式,簡化為某個值。compose是柯里化函式,藉助於Reduce來實現,將多個函式合併到一個函式返回,主要是在middleware中被使用。
6、applyMiddleware.js
applyMiddleware.js檔案提供了middleware中介軟體重要的API,middleware中介軟體主要用來對store.dispatch進行重寫,來完善和擴充套件dispatch功能。
那為什麼需要中介軟體呢?
首先得從Reducer說起,之前 Redux三大原則裡面提到了reducer必須是純函式,下面給出純函式的定義:
-
對於同一引數,返回同一結果
-
結果完全取決於傳入的引數
-
不產生任何副作用
至於為什麼reducer必須是純函式,可以從以下幾點說起?
-
因為 Redux 是一個可預測的狀態管理器,純函式更便於 Redux進行除錯,能更方便的跟蹤定位到問題,提高開發效率。
-
Redux 只透過比較新舊物件的地址來比較兩個物件是否相同,也就是透過淺比較。如果在 Reducer 內部直接修改舊的state的屬性值,新舊兩個物件都指向同一個物件,如果還是透過淺比較,則會導致 Redux 認為沒有發生改變。但要是透過深比較,會十分耗費效能。最佳的辦法是 Redux返回一個新物件,新舊物件透過淺比較,這也是 Reducer是純函式的重要原因。
Reducer是純函式,但是在應用中還是會需要處理記錄日誌/異常、以及非同步處理等操作,那該如何解決這些問題呢?
這個問題的答案就是中介軟體。可以透過中介軟體增強dispatch的功能,示例(記錄日誌和異常)如下:
五、從零開始實現一個簡單的Redux
既然是要從零開始實現一個Redux(簡易計數器),那麼在此之前我們先忘記之前提到的store、Reducer、dispatch等各種概念,只需牢記Redux是一個狀態管理器。
首先我們來看下面的程式碼:
此時我們對count進行修改,所有的listeners都會收到通知,並且能做出相應的處理。但是目前還會存在其它問題?比如說目前state只含有一個count欄位,如果要是有多個欄位是否處理方式一致。同時還需要考慮到公共程式碼需要進一步封裝,接下來我們再進一步最佳化:
我們可以從程式碼看出,最終我們提供了三個API,是不是與之前Redux原始碼中的核心入口檔案index.js比較類似。但是到這裡還沒有實現Redux,我們需要支援新增多個欄位到state裡面,並且要實現Redux計數器。
透過測試,我們發現目前已經支援了state裡面存多個屬性欄位,接下來我們把之前changeState改造一下,讓它能支援自增和自減。
//自增store.changeState({ count: store.getState().count + 1});//自減store.changeState({ count: store.getState().count - 1});//隨便改成什麼store.changeState({ count: 金融 });
我們發現可以透過changeState自增、自減或者隨便改,但這其實不是我們所需要的。我們需要對修改count做約束,因為我們在實現一個計數器,肯定是隻希望能進行加減操作的。所以我們接下來對changeState做約束,約定一個plan方法,根據type來做不同的處理。
function plan (state, action) => { switch (action.type) { case 'INCREMENT': return { ...state, count: state.count + 1 } case 'DECREMENT': return { ...state, count: state.count - 1 } default: return state } }let store = createStore(plan, initState);//自增store.changeState({ type: 'INCREMENT'});//自減store.changeState({ type: 'DECREMENT'});
我們在程式碼中已經對不同type做了不同處理,這個時候我們發現再也不能隨便對state中的count進行修改了,我們已經成功對changeState做了約束。我們把plan方法做為createStore的入參,在修改state的時候按照plan方法來執行。到這裡,恭喜大家,我們已經用Redux實現了一個簡單計數器了。
這就實現了 Redux?這怎麼和原始碼不一樣啊
然後我們再把plan換成reducer,把changeState換成dispatch就會發現,這就是Redux原始碼所實現的基礎功能,現在再回過頭看Redux的資料流圖是不是更加清晰了。
六、Redux Devtools
Redux devtools是Redux的除錯工具,可以在Chrome上安裝對應的外掛。對於接入了Redux的應用,透過 Redux devtools可以很方便看到每次請求之後所發生的改變,方便開發同學知道每次操作後的前因後果,大大提升開發除錯效率。
如上圖所示就是 Redux devtools的視覺化介面,左邊操作介面就是當前頁面渲染過程中執行的action,右側操作介面是State儲存的資料,從State切換到action皮膚,可以檢視action對應的 Reducer引數。切換到Diff皮膚,可以檢視前後兩次操作發生變化的屬性值。
七、總結
Redux 是一款優秀的狀態管理器,原始碼短小精悍,社群生態也十分成熟。如常用的react-redux、dva都是對 Redux 的封裝,目前在大型應用中被廣泛使用。這裡推薦透過
以及
來學習它核心的思想,進而提升閱讀原始碼的能力。
來自 “ ITPUB部落格 ” ,連結:http://blog.itpub.net/69912579/viewspace-2677179/,如需轉載,請註明出處,否則將追究法律責任。
相關文章
- 深入理解reduxRedux
- 深入淺出redux學習Redux
- 深入淺出理解ReduxRedux
- 學習 redux 原始碼整體架構,深入理解 redux 及其中介軟體原理Redux原始碼架構
- 深入理解 Redux 中介軟體Redux
- 深入理解深度學習深度學習
- 深入理解redux之從redux原始碼到react-redux的原理Redux原始碼React
- redux深入理解之中介軟體(middleware)Redux
- 深入淺出React和ReduxReactRedux
- linux shell陣列深入學習理解Linux陣列
- 深入學習和理解Django模板層:構建動態頁面Django
- 【學習筆記】CSS深入理解之margin筆記CSS
- 【學習筆記】CSS深入理解之overflow筆記CSS
- 【學習筆記】CSS深入理解之relative筆記CSS
- Redux學習手冊Redux
- 深入學習和理解Django檢視層:處理請求與響應Django
- 深入理解redux之reducer為什麼是純函式Redux函式
- 【學習筆記】CSS深入理解之vertical-align筆記CSS
- 深入理解JVM(③)學習Java的記憶體模型JVMJava記憶體模型
- 深入理解計算機系統-學習筆記 (1)計算機筆記
- redux-saga學習筆記Redux筆記
- [譯] 深入理解 Props 和 State
- 深入理解原型和原型鏈原型
- Blocks深入理解和詳解BloC
- 學習記錄--《深入理解ES6》之函式(上)函式
- 深入學習SpringMVCSpringMVC
- 深入學習 vueVue
- 深入學習synchronizedsynchronized
- 理解Redux中介軟體Redux
- 深入學習js之——call和apply#10JSAPP
- 深入學習Semantic Kernel:建立和配置prompts functionsFunction
- 深入學習js之——原型和原型鏈#1JS原型
- 深入淺出redux-middlewareRedux
- 帶你深入淺出理解深度學習(附資源打包下載)深度學習
- 《深入理解計算機系統原理》學習筆記與習題答案(一)計算機筆記
- 深度學習 SSD的理解和細節分析深度學習
- Java中Thread 和Runnable 深入理解Javathread
- 深入理解原型物件和原型鏈原型物件