Redux技術架構簡介

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

1. 解決的問題

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

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

2.三大原則

  • 單一數據源
    整個應用的 state 被儲存在一棵 object tree 中,而且這個 object tree 只存在於惟一一個 store 中。
  • State是隻讀的

    惟一改變 state 的方法就是觸發 action,action 是一個用於描述已發生事件的普通對象。編程

  • Reducer必須是純函數

3.基本概念

  • Actionredux

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

    {type: SET_ALL,index: 5}
  • 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的函數以進一步簡化。併發

    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);
  • Reducer
    Reducer 指定了應用狀態的變化如何響應 actions 併發送到 store 的,action只是告訴Redux該幹什麼了,並無告訴他怎麼幹,而reducer就是根據action處理state如何改變的邏輯。
    Reducer必須是一個純函數(緣由稍後解釋),他根據action處理state的更新,若是沒有更新或遇到未知action,則返回舊state;不然返回一個新state對象。注意:不能修改舊state,必須先拷貝一份state,再進行修改,也可使用Object.assign函數生成新的state。另外,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};
            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值會怎麼樣呢?ide

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

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

clipboard.png

經過查看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
    });
  • Store
    Store 是把Action、Reducer聯繫到一塊兒的對象。Store 有如下職責:

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

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

    const store  = createStore(
            indexPhotomainReducer,
        );
    export default store;

    (2)State
    應用的state也應該只有一個,裏面包含了全部的應用數據。當須要獲取state的時候,須要使用store.getState()方法。
    (3)store.dispatch(action)
    UI更新state 的惟一途徑,經過dispatch方法發起action,喚起對應的reducer更新state。
    (4)store. subscribe(listener)
    經過此方法能夠設置監聽函數,一旦state發生變化,就會當即調用監聽函數(listener)。同時,subscribe的返回值(其實是一個unsubscribe函數)能夠解除監聽。如:

    // 每次 state 更新時,打印日誌
    // 注意 subscribe() 返回一個函數用來註銷監聽器
    const unsubscribe = store.subscribe(() =>
        console.log(store.getState())
    )
    
    // 中止監聽 state 更新
    unsubscribe();

4.數據流

clipboard.png

(1)首先,用戶發出 Action(如click事件)。

store.dispatch(SHOW_SLIDER)

(2) 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;
   }
    }

(3) State 一旦有變化,Store 就會調用監聽函數。

store.subscribe(listener);

(4) listener能夠經過store.getState()獲得當前狀態。若是使用的是 React,這時能夠觸發從新渲染 View。

function listerner() {
  let newState = store.getState();
  component.setState(newState);   
}

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

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

參考

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

相關文章
相關標籤/搜索