Redux技術架構簡介(一)

Redux是一個程序架構,源於Flux(Facebook提出的一種架構),然而,它不只能夠應用於React,還能夠應用於其餘任何框架中。值得一提的是,Redux的源代碼不多,可是他的邏輯拆分和函數式編程的設計思想是很是值得學習的。javascript

1. 解決的問題

當一個JavaScript單頁應用變得愈來愈複雜時,咱們要處理的數據(model)也變得愈來愈龐大,隨之而來的是每一個數據的狀態(state)會變得難以維護。當一個model改變時,咱們可能要手動處理由此引起的其餘model的變化,更糟糕的是,其餘model變化可能又會引發另外一些model的變化,這樣產生連鎖反應。最後咱們很容易就會不記得model在何時以及如何發生改變。這正是Redux能夠解決的最大痛點之一—以統一的方式管理數據狀態。html

統一的數據狀態管理體現爲如下2個方面:java

  • 組件間的數據通訊
  • UI和數據處理邏輯分離

2. 三大原則

  • 單一數據源
    整個應用的 state 被儲存在一棵 object tree 中,而且這個 object tree 只存在於惟一一個 store 中。
  • State是隻讀的
    惟一改變 state 的方法就是觸發 action,action 是一個用於描述已發生事件的普通對象。
  • Reducer必須是純函數的

3. 基本概念

(1) Action

Action 是把數據從應用傳到 store 的有效載荷。它是 store 數據的惟一來源。直白的說,action就是一種消息類型,他告訴Redux是時候該作什麼了,並帶着相應的數據傳到Redux內部(即接下來介紹的Reducer)。
Action就是一個簡單的對象,其中必需要有一個type屬性,用來標誌動做類型(reducer以此判斷要執行的邏輯),其餘屬性用戶能夠自定義,但要儘可能簡單。如:
{type: SET_ALL,index: 5}react

(2) Action Creator

Action Creator是一個用來自動生成Action的函數,這也是Redux函數式編程的一種體現。一般,咱們會按照業務邏輯或組件把相關的Action Creator放在一個文件中,形式以下:編程

export const SHOW_SLIDER = 'SHOW_SLIDER';
export const RIGHT_SLIDER = 'RIGHT_SLIDER';
export const showSliderAction = (index) => {
   return {
       type: SHOW_SLIDER,
       index
   };
}
export const rightSliderAction = (length) => {
   return {
       type: RIGHT_SLIDER,
       length
   };
} 
複製代碼

其中showSliderAction和rightSliderAction就是Action Creator。
因爲Action Creator的形式大致相同,咱們還能夠建立一個用來生成Action Creator的函數以進一步簡化。redux

export const makeCreateAction = (type, ...keys) => {
    return (...data) => {
        let action = {type};
        keys.forEach((v,i) => action[v] = data[i]);
        return action;
    }
}
export const showSliderAction = makeCreateAction(SHOW_SLIDER, index);
export const rightSliderAction = makeCreateAction(RIGHT_SLIDER, length);
複製代碼

(3) Reducer

Reducer 指定了應用狀態的變化如何響應 actions 併發送到 store 的,action只是告訴Redux該幹什麼了,並無告訴他怎麼幹,而reducer就是根據action處理state如何改變的邏輯。
Reducer必須是一個純函數(緣由稍後解釋),他根據action處理state的更新,若是沒有更新或遇到未知action,則返回舊state;不然返回一個新state對象。注意:不能修改舊state,必須先拷貝一份state,再進行修改,也可使用Object.assign函數生成新的state。另外,state參數需先進行初始化。實例代碼以下:bash

//初始狀態
let initialState = {hiddenClass: 'g-hidden',currentIndex:0};
let sliderReducer = function (state = initialState, action) {
    switch(action.type){
        case sliderAction.SHOW_SLIDER:
            return {hiddenClass: '',currentIndex:action.index};
        case sliderAction.RIGHT_SLIDER:
            if(state.currentIndex == action.length-1){
                return Object.assign({}, state, {currentIndex:0});
            }else{
                return Object.assign({}, state, {currentIndex:Number.parseInt(state.currentIndex)+1});
            }
        default:
            return state;
    }
}

export default sliderReducer;
複製代碼
  • 使用純函數的緣由:
    首先,純函數的特色是:架構

    • 函數的返回結果只依賴於它的參數。
    • 函數執行過程裏面沒有反作用。(不會對外界產生影響)

    若是不使用純函數,即直接更改state值會怎麼樣呢?併發

… …      
    case sliderAction.RIGHT_SLIDER:
        if(state.currentIndex == action.length-1){
    		state.currentIndex = 0;
            return state;
        }
    … …
複製代碼

以後會發現,不管state如何變化,UI都不會更新。如下是Redux的部分源碼:框架

經過查看Redux源碼得知,新舊state的比較只是對引用地址的比較,若是reducer只是返回舊state(即previousStateForKey)的更新,新state(nextStateForKey)實際上和舊state引用的都是同一塊內存地址,因此不管如何更改,新舊state始終保持相同。這就是爲何reducer必須是純函數的緣由。

  • Reducer拆分與合併:
    Reducer 函數負責生成 State。因爲整個應用只有一個 State 對象,包含全部數據,對於大型應用來講,這個 State 必然十分龐大,致使 Reducer 函數也十分龐大。因此須要先對reducer進行拆分,拆分的原則能夠按業務邏輯進行劃分,若是是react的話,能夠直接和react的組件相對應進行劃分。
    劃分好以後,能夠用Redux提供的combineReducers方法進行合併,十分方便。
    import { combineReducers } from 'redux';
    import photomainReducer from './photomainReducer';
    import sortReducer from './sortReducer';
    import sliderReducer from './photoSliderReducer';
    
    export default combineReducers({
        photomainReducer,
        sortReducer,
        sliderReducer
    });
    複製代碼

    (3) Store

    Store 是把Action、Reducer聯繫到一塊兒的對象。Store 有如下職責:

    1.維持應用的 state;
    2.提供 getState() 方法獲取 state;
    3.提供 dispatch(action) 方法更新 state;
    4.經過 subscribe(listener) 註冊監聽器;
    5.經過 subscribe(listener) 返回的函數註銷監聽器。

    建立store

    Redux應用應該只有一個store,他提供了建立store的API—createStore(reducer, initState)。第一個參數爲一個reducer,能夠接受經過combineReducers合併後的reducer,第二個是可選參數,意思是能夠設置應用的初始state。
    const store  = createStore(
            indexPhotomainReducer,
        );
    export default store;
    複製代碼

    State

    應用的state也應該只有一個,裏面包含了全部的應用數據。當須要獲取state的時候,須要使用store.getState()方法。

    store.dispatch(action)

    UI更新state 的惟一途徑,經過dispatch方法發起action,喚起對應的reducer更新state。

    store. subscribe(listener)

    經過此方法能夠設置監聽函數,一旦state發生變化,就會當即調用監聽函數(listener)。同時,subscribe的返回值(其實是一個unsubscribe函數)能夠解除監聽。如:
    // 每次 state 更新時,打印日誌
    // 注意 subscribe() 返回一個函數用來註銷監聽器
    const unsubscribe = store.subscribe(() =>
       console.log(store.getState())
    )
    
    // 中止監聽 state 更新
    unsubscribe();
    複製代碼

4. 數據流

  • 首先,用戶發出 Action(如click事件)。
store.dispatch(SHOW_SLIDER)
複製代碼
  • Store 自動調用 Reducer,而且傳入兩個參數:當前 State 和收到的 Action。 Reducer根據Action的type調用相應的分支, 返回新的 State 。
let initialState = {hiddenClass: 'g-hidden',currentIndex:0};

let sliderReducer = function (state = initialState, action) {
   switch(action.type){
       case sliderAction.SHOW_SLIDER:
           return {hiddenClass: '',currentIndex:action.index};
       default:
           return state;
   }
	}
複製代碼
  • State 一旦有變化,Store 就會調用監聽函數。
store.subscribe(listener);
複製代碼
  • listener能夠經過store.getState()獲得當前狀態。若是使用的是 React,這時能夠觸發從新渲染 View。
function listerner() {
  let newState = store.getState();
  component.setState(newState);   
}
複製代碼

以上爲Redux的數據流動過程。

本篇到此告一段落,下一篇介紹Redux的異步實現

參考

Redux 中文文檔
Redux 入門教程-阮一峯

相關文章
相關標籤/搜索