解讀只有99行的Redux(一)—— 從創建一個store開始

redux

原文發布在我的個人博客 解讀只有99行的Redux(一) | 以太空間

一、概述

??隨著React這個極具革命式函數式思想的前端框架的誕生,Flux模式的前端狀態管理框架也隨之出現,其中比較著名的就是Flux、Redux和Mbox。Flux是Facebook開發的一種設計模式,旨在保持數據單向流動,當然Flux也存在一些小問題,所以Redux和其他的類Flux庫應運而生,它們在實現了Flux思想的同時又具備了自己的特點。
??Redux由Dan Abramov和Andrew Clark一起開發,因為Redux的緣故,它們都被邀請加入了Facebook的React團隊。在Dan Abramov的github gist頁面上有一個slim-redux(代碼附在本文末尾),去除了一些復雜的類型判斷和錯誤處理代碼,但完整地實現了Redux的所有功能,我們下面就來對這個只有99行的Redux進行解讀。

二、createStore解讀

??我們使用Redux時候最頻繁使用的就是這個createStore函數了,一般我們都會這樣使用

import { createStore } from 'redux';
const store = createStore(reducer);

??reducer這個參數是用戶事先定義的數據狀態處理函數,一般會以類似下面的方式聲明:

// reducer接受state和action并返回新的state
function reducer (state = [], action) {
  switch (action.type) {
    case 'ADD_TODO':
      return [
        ...state,
        {
          id: action.id,
          text: action.text,
          completed: false
        }
      ];
    default:
      return state;
  }
};

??reducer的主體是一個switch結構的運算,它只是根據傳入的狀態數據stateaction來判斷返回一個新的state。reducer必須是一個純函數,純函數主要的含義就是它不可以修改影響輸入值,并且沒有副作用,副作用指的是例如函數中一些異步調用或者會影響函數作用域之外的變量一類的操作。
??另外,注意我們返回新的state時使用的展開操作符的方法,在上面的示例中,這樣返回的是一個全新的數組,而不是修改了傳入的數組。這也就是我們使用 React 技術棧時尤其需要注意的一點:保證數據的immutability不可變性。

??相信大家也知道,createStore這個函數還有第二個的參數,是store的初始狀態,也就是說craeteStore函數原型是這樣的

function createStore(reducer, initialState) {...}

??然后再來看createStore內部這段代碼:

var currentReducer = reducer;
var currentState = initialState;
var listeners = [];
var isDispatching = false;

function getState() {
    return currentState;
}

??前兩行就是將傳入的reducer參數和state參數進行保存,第三行聲明了保存監聽函數的數組,第四行的isDispatching是個標識型變量,具體用途下面會說到。
??我們知道,當我們調用createStore函數之后,其返回結果(一般寫作store)有個獲取當前store內部數據狀態的成員函數getState,該成員函數實現方法就是直接return內部的currentState,so easy~

??接著看subscribe函數的實現:

function subscribe(listener) {
  listeners.push(listener);
  
  return function unsubscribe() {
    var index = listeners.indexOf(listener);
    listeners.splice(index, 1);
  };
}

??當開發者對store調用subscribe函數的時候,其內部會把傳入的監聽函數listener參數push到專門存放監聽函數的數組listeners里,然后返回一個能取消傳入的監聽函數的函數unsubscribe作為返回結果,這個unsubscribe實現原理就是將listener記錄下來,然后在調用的時候將其從listeners數組里面剔除。

??接下來是最重量級選手dispatch函數的實現代碼:

function dispatch(action) {
  if (isDispatching) {
    throw new Error('Reducers may not dispatch actions.');
  }
  
  try {
    isDispatching = true;
    currentState = currentReducer(currentState, action);
  } finally {
    isDispatching = false;
  }
  
  listeners.slice().forEach(listener => listener());
  return action;
}

??首先來解釋一下前面提到的isDispatching,當開發者對store派發(dispatch)一個action時,dispatch內部會調用reducercurrentReducer)并利用傳入的actionstore內部的狀態數據(currentState)進行處理,但是這個過程可能是比較耗時的,所以為了避免對正在進行reducer處理的store再次進行reducer處理,專門用isDispatching來標識當前store內部是否正在進行reducer處理,如果isDispatchingtrue的話,就不會再對store進行reducer處理,起到一個加鎖的作用。
??所以disptach內部會首先檢測isDispatching是否正處于一個鎖死的狀態,如果isDispatchingtrue,說明鎖死,正在進行reducer處理,就直接拋出錯誤。然后在try塊內部,首先將isDispatching置為true,對其加鎖,再對store內部的狀態數據進行處理,處理完后再把isDispatching置為false,將鎖打開。
??最后遍歷listeners,調用所有的監聽函數,并返回action。

??下面這部分是createStore函數的剩余部分:

function replaceReducer(nextReducer) {
  currentReducer = nextReducer;
  dispatch({ type: '@@redux/INIT' });
}
  
dispatch({ type: '@@redux/INIT' });
  
return { dispatch, subscribe, getState, replaceReducer };

??在調用createStore的時候,其內部就派發(dispatch)一個初始action({ type: '@@redux/INIT' }),進行一些初始化的操作。此外,store還有一個用的比較少的成員函數replaceReducer,作用是替換reducer函數,原理很簡單,就是將currentReducer指向新的reducer函數,并再次派發一個初始action。
??上面這部分代碼的最后一行羅列出了store所有成員函數。

??以下是解讀只有99行的Redux系列的其他兩篇文章

??解讀只有99行的Redux(二) 中間件相關
??解讀只有99行的Redux(三) 輔助函數和組合Reducer

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。

推薦閱讀更多精彩內容