理解 Redux 的原理有助於我們更好的使用它。本文實現 Redux 的基本 API。
Redux 嘗試讓 state 的變化變得可預測。
redux 已經被越來越多的人使用,理解其原理有助於更好的使用它。閱讀 redux 源碼是一個不錯的辦法,當我們了解了其原理之後,現在來實現一個簡單的 redux 吧。
本文完整代碼請查看 Github:https://github.com/YanYuanFE/redux-app
// clone repo
git clone https://github.com/YanYuanFE/redux-app.git
cd redux-app
// checkout branch
git checkout part-3
// install
npm install
// start
npm start
基本 API#
如果你使用過 redux,應該對 redux 的 API 了然於胸吧。redux 的基本 API 包括 createStore、getState、subscribe、dispatch。現在回顧一下 redux 的使用方法:
首先,redux 暴露出 createStore 方法,調用 createStore 方法傳入 reducer,使用 const store = createStore (reducer),(此處暫時不考慮中間件),使用 store.getState () 獲取狀態,使用 store.dispatch () 發起一個 action,使用 store.subscribe () 訂閱狀態改變時執行的函數。下面是 redux API 的簡單實現。
為了方便調試,使用前面講的 redux 實現簡單計數器的代碼,在 src 目錄下新建 redux.js,
export function createStore(reducer) {
}
在 redux.js 中,先導出 redux 的核心方法 createStore,傳入 reducer 作為參數。
在 createStore 方法中,需要定義用於保存當前狀態的變量以及用於保存狀態改變後執行函數的監聽器。
let currentState;
let currentListeners = [];
此處定義 currentState 為當前狀態,初始化為 undefined; 定義 currentListenners 為監聽器,初始化為數組。
然後,定義 getState 方法,用於獲取當前狀態,直接返回 currentState:
function getState() {
return currentState;
}
然後,定義 subscribe 函數,用於訂閱狀態改變時執行的方法:
function subscribe(listener) {
currentListeners.push(listener);
}
subscribe 方法傳入一個監聽函數,將監聽函數 push 進監聽器數組中。
然後定義 dispatch 方法,用於發起 action:
function dispatch(action) {
currentState = reducer(currentState, action);
currentListeners.forEach(v => v());
return action;
}
dispatch 方法傳入 action,然後調用 reducer 開始更新 currentState,傳入當前 currentState 和 action,當狀態改變時,通知監聽器,監聽器數組依次執行數組中的每一個訂閱方法,此處使用了設計模式中的發布 —— 訂閱模式,然後返回 action。
可以看到,上面已經實現了 Redux 的基本 API,那麼就結束了嗎?當然沒有,因為 redux 並沒有初始化,reducer 中的初始狀態並沒有生效,所以需要手動發起一個 action,並且 action.type 必須是獨一無二的。如下:
dispatch({type: '@@REDUX/INIT'});
此處,對 redux 進行初始化,定義 type 為 '@@REDUX/INIT',這樣定義的原因是保證命中 reducer 中 action.type 為 default 使其返回初始化 state。
最後,根據 redux 使用方法可以知道,store 肯定是一個對象,對象包含 getState、subscribe、dispatch 等方法,所有,最後需要將上述方法返回。
return { getState, subscribe, dispatch };
至此,一個超級簡單的 redux 就實現了,麻雀雖小,五臟俱全。這個 redux 雖然簡單,省去了許多錯誤處理過程,但是對於理解 redux 足矣。下面是完整代碼:
export function createStore(reducer) {
let currentState;
let currentListeners = [];
function getState() {
return currentState;
}
function subscribe(listener) {
currentListeners.push(listener);
}
function dispatch(action) {
currentState = reducer(currentState, action);
currentListeners.forEach(v => v());
return action;
}
dispatch({type: '@@REDUX/INIT'}); //初始化
return { getState, subscribe, dispatch }
}
測試#
下面結合之前的計數器例子來驗證上述 redux 是否正確。
在前面計數器例子中,打開 src 下 index.js,修改如下代碼:
import { createStore } from './redux';
替換 redux 為剛剛編寫的 redux.js 文件。查看瀏覽器運行結果。
如圖,與 redux 的效果一致,達到預期效果。
總結#
本文實現了一個簡單的 redux,完成其基本 API 的實現,這有助於你理解 redux 的原理,後面會逐步對其擴展,編寫 react-redux 以及中間件的實現。