實例講解Redux
什么是Redux
隨著 JavaScript 單頁應用開發日趨復雜, JavaScript 需要管理比任何時候都要多的 state (狀態) 。 這些 state 可能包括服務器響應、緩存數據、本地生成尚未持久化到服務器的數據,也包括 UI 狀態,如激活的路由,被選中的標簽,是否顯示加載動效或者分頁器等等。
簡單來說 Redux 就是 javaScript 的狀態管理器。同時, Redux 支持 React 、 Angular 、 Ember 、 jQuery 等。
到這里你可能并不知道該如何使用 Redux ,別著急,在了解如何使用之前,需要先理解相關的基本概念。示例代碼可查看 demo1/app.js
State
在一個可以進行用戶交互的界面上,當我們觸發了交互事件的時候,界面也會跟著進行更新。例如在一個app中的下拉刷新,當我們進行下拉刷新這個動作時,會去請求服務器數據,當返回結果時app的頁面會更新。界面的改變我們就可以看做是狀態的變化。而狀態的變化是由于我們進行了某個動作。在Redux中,尤其需要注意的是 State是只讀的 ,且狀態的變化只能通過Reducer函數進行轉換,
Action
Action就是你與界面進行交互的行為。它是一個對象且 必須要包含一個 type 屬性 ,用來描述你要做的是什么事。
Store
Store 就是應用狀態的容器。用來維護應用的 state 、獲取 state 、更新 state 、監聽 state 變化等。這里需要注意的是 整個應用只有一個 Store
Reducer
一個純函數,接收當前的 state 和一個 action 參數,根據當前的行為,即 action 的 type 屬性返回一個新的 state 。狀態的更新只能通過這個函數。
Talk is cheap, Show me the code!
可能看了上述內容你仍然不知道什么是 Redux , Redux 能干什么 。下面結合實例 demo1/app.js 中的內容理解上述概念。
1.引入相關模塊(這里沒有使用 ES6 的方式是為了能使用 node 命令直接查看結果, node 命令無法識別 import 。當然你也可以通過配置 babel 的方式使用 import 語法)
var { createStore } = require('redux');
//import { createStore } from 'redux' //es6方式引入
2.定義 Reducer ,來進行狀態的更新
var defaultState = 'JavaScript'
function programLanguage(state = defaultState, action) {
switch (action.type) {
case 'IOS':
return action.language;
case 'WEB':
return action.language;
case 'SNACK':
return action.language;
default:
return state;
}
}
從上述代碼可以看到,我們定義了一個 programLanguage 方法,其實這就是一個 Reducer ,它接受了當前 state 和 action 參數。用來更新 state 。
3.創建 store 。
let store = createStore(programLanguage);
再次強調整個應用中只能有一個 store ,即 createStore 函數只能被調用一次。
createStore 的定義如下:
function createStore(reducer, preloadedState, enhancer);
可以看到的是createStore接受三個參數
-
reducer:就是上述的reducer函數,這里不再贅述
-
preloadedState: 初始state
-
enhancer:組合store creator的高級函數,返回一個新的強化過的store creator。
4.更改應用的狀態
store.dispatch({ type: 'default' });
store.dispatch({ language: 'Swift', type: 'IOS' });
store.dispatch({ language: 'Ruby', type: 'WEB' });
store.dispatch({ language: 'Python', type: 'SNACK' });
通過 dispatch 方法將 action 發送到 reducer 函數中,進行狀態的更新。
執行 node demo1/app.js 的打印結果如下
JavaScript
JavaScript Swift
JavaScript Swift Ruby
JavaScript Swift Ruby Python
當執行第一句代碼 store.dispatch({ type: 'default' }); 時 返回的是JavaScript。是由于dispatch方法內部會調用在 createStore 中注冊的 Reducer 函數,即 programLanguage 函數。由 programLanguage 的內部邏輯 return 的是一個 defaultState 。此時整個應用的狀態為 JavaScript
當執行第二句代碼 store.dispatch({ language: 'Swift', type: 'IOS' }); 時 返回了 JavaScript Swift 。是由于 type 為 IOS , programLanguage 返回的是 state + ' ' +action.language; 因為上一句代碼將應用的狀態變為了 JavaScript ,所以結果自然就是 JavaScript Swift 。同時整個應用的狀態也會變成 JavaScript Swift 。
同理,第三句和第四句代碼也是如此。
5.訂閱狀態的更新
store.subscribe(() =>
console.log(store.getState())
);
為了方便查看狀態的變化。這里調用了 subscribe 方法,每當狀態發生變化時都會調用其回調函數。
store.getState() 獲取應用的當前狀態。
優化操作
Action creator
隨著頁面的增加, Action 必然也會越來越多,如果 Action 內部所攜帶的消息也很多。此時如果需要狀態的更新,就需要寫復雜的 Action ,造成代碼可讀性差,文件臃腫。所以我們可以使用 Action creator 來構造 Action 。當需要進行狀態更新就傳入相應的數據到 Action creator 中生成 Action 并返回。
在 demo2 中
我們創建了一個 HomeAction 的文件專門用來存放 Action Creator 。在此文件中我們創建了三個 Action creator ,分別是 action_ios 、 action_web 、 action_snack ,并將創建 Action 的任務交給這三個函數。
與此同時在我們 demo2/app.js 中需要 dispatch 一個 action 的時候。只需要從 Action creator 中取出 Action 就行了。并不需要我們手動的去創建,如此一來便大大增加代碼的可讀性,可維護性。
分散Reducer
同樣的隨著業務量的增加, Reducer 必定也會越來越大。所以我們可以按模塊的不同來拆分Reducer。將一個大的 Reducer 拆分成幾個小的 reducer 。
在 demo3 中 reducer 文件夾中,我們按照模塊的不同的拆分 homeReducer 和 profileReducer 。分別用來處理 home 頁面和 profile 頁面狀態的更新。
那么問題來了,這么拆分如何使用 createStore 去創建 store 呢,畢竟 createStore 只能傳一個 Reducer 參數。而且 createStore 只能調用一次。
別著急,在 redux 中提供了一個 combineReducers 的方法,用來組合我們的 Reducer 。
const mainReducer = combineReducers({
homeReducer,
profileReducer
})
let store = createStore(mainReducer);
來自:https://segmentfault.com/a/1190000007515109