react-redux 是 React 生態中比較(若是不是最的話)流行的一種狀態管理方式,而它所依託的 redux,繼承了 flux 的衣鉢,而且引入了單一數據源、state 爲只讀、只能經過純函數修改三大原則。一個最簡單的 redux 應用示例:javascript
// https://cn.redux.js.org/
import { createStore } from 'redux'
/** * 這是一個 reducer,形式爲 (state, action) => state 的純函數。 * 描述了 action 如何把 state 轉變成下一個 state。 * * state 的形式取決於你,能夠是基本類型、數組、對象、 * 甚至是 Immutable.js 生成的數據結構。唯一的要點是 * 當 state 變化時須要返回全新的對象,而不是修改傳入的參數。 * * 下面例子使用 `switch` 語句和字符串來作判斷,但你能夠寫幫助類(helper) * 根據不一樣的約定(如方法映射)來判斷,只要適用你的項目便可。 */
function counter(state = 0, action) {
switch (action.type) {
case 'INCREMENT':
return state + 1
case 'DECREMENT':
return state - 1
default:
return state
}
}
// 建立 Redux store 來存放應用的狀態。
// API 是 { subscribe, dispatch, getState }。
let store = createStore(counter)
// 能夠手動訂閱更新,也能夠事件綁定到視圖層。
store.subscribe(() => console.log(store.getState()))
// 改變內部 state 唯一方法是 dispatch 一個 action。
// action 能夠被序列化,用日記記錄和儲存下來,後期還能夠以回放的方式執行
store.dispatch({ type: 'INCREMENT' })
// 1
store.dispatch({ type: 'INCREMENT' })
// 2
store.dispatch({ type: 'DECREMENT' })
// 1
複製代碼
createStore
是 redux 中最基礎的 API,它的做用就是返回一個 store
;而這個 store
有三個方法 —— dispatch
, subscribe
和 getState
。其實從方法名上,咱們大體能夠推測出 redux 應用了訂閱發佈模式;該模式能夠直接套用下面的模板代碼:java
// 開始
// createStore 是一個閉包,維護了 currentState, listeners 私有性;
// 外層想要獲取 state,只能經過 getState;
// listeners 沒法從外層修改
function createStore(reducer, preloadedState) {
// 每次調用 subscribe,就會向 listeners 數組 push 一個 listener;
// 每次調用 dispatch,listeners 數組中的函數會依次執行;
const listeners = [];
let currentReducer = reducer;
let currentState = preloadedState;
function dispatch(action) {
// 正如示例代碼中所展示的,reducer(即 counter) 函數接受 state,action 兩個參數
// action 是一個簡單對象,有一個 type 屬性;
// reducer 中根據 type 執行對應操做,返回新的 state;
currentState = currentReducer(currentState, action);
// 執行 listeners 數組中的全部函數
listeners.forEach(listener => listener());
}
function subscribe(listener) {
listeners.push(listener);
// 返回一個取消訂閱的函數
return function unsubscribe() {
let index = listeners.indexOf(listener);
listeners.splice(index, 1);
};
}
// 這個函數的做用就是返回 state
function getState() {
return currentState;
}
return {
dispatch,
subscribe,
getState
};
}
function counter(state = 0, action) {
switch (action.type) {
case 'INCREMENT':
return state + 1;
case 'DECREMENT':
return state - 1;
default:
return state;
}
}
// 測試一下
let store = createStore(counter);
store.subscribe(() => console.log(store.getState()));
store.dispatch({ type: 'INCREMENT' });
// 1
store.dispatch({ type: 'INCREMENT' });
// 2
store.dispatch({ type: 'DECREMENT' });
// 1
複製代碼
上面的代碼只是爲了大體解釋一下 redux 的運行機制,實際上是有很多問題的,好比 action 只能是一個簡單對象,但咱們的代碼中並無進行檢測;咱們暫且不去關注這些問題,而是先去實現功能。接下來,修改 dispatch,使它支持函數參數:react
function dispatch(action) {
// 若是傳入一個函數,則爲這個函數注入 dispatch 和 getState 方法,
// 而且執行這個 action
if (typeof action === 'function') {
return action(dispatch, getState);
}
// 正如示例代碼中所展示的,reducer(即 counter) 函數接受 state,action 兩個參數
// action 是一個簡單對象,有一個 type 屬性;
// reducer 中根據 type 執行對應操做,返回新的 state;
currentState = currentReducer(currentState, action);
// 執行 listeners 數組中的全部函數
listeners.forEach(listener => listener());
return action;
}
複製代碼
用異步的方式,測試一下:redux
function counter(state = 0, action) {
switch (action.type) {
case 'INCREMENT':
return state + 1;
case 'DECREMENT':
return state - 1;
default:
return state;
}
}
function asyncAdd(dispatch) {
return new Promise((resolve, reject) => {
setTimeout(() => {
resolve(
dispatch({
type: 'INCREMENT'
})
);
}, 1000);
});
}
// 測試一下
let store = createStore(counter);
store.subscribe(() => console.log(store.getState()));
store.dispatch({ type: 'INCREMENT' });
// 1
store.dispatch({ type: 'INCREMENT' });
// 2
store.dispatch(asyncAdd).then(() => store.dispatch(asyncAdd)); // 1s 後 3, 2s 後 4
複製代碼
如今,咱們的代碼已經具有了 redux + redux-thunk 的功能,並且沒有增長新的 API。但咱們是用入侵的方式修改了 dispatch,若是要增長其餘新功能怎麼辦?繼續修改 dispatch 麼?顯然不能這麼幹。redux 使用中間件的機制,來修改 dispatch:數組
function compose(...funcs) {
if (funcs.length === 0) {
return args => args;
}
if (funcs.length === 1) {
return funcs[0];
}
return funcs.reduce((a, b) => (...args) => a(b(...args)));
}
function applyMiddleware(...middlewares) {
return createStore => (...args) => {
const store = createStore(...args);
let dispatch = () => {
throw new Error('not valid');
};
const middlewareAPI = {
getState: store.getState,
dispatch: (...args) => dispatch(...args)
};
const chain = middlewares.map(middleware => middleware(middlewareAPI));
// applyMiddleware 本質上,仍是須要去覆蓋原來的 dispatch 方法
dispatch = compose(...chain)(store.dispatch);
return {
...store,
dispatch
};
};
}
// 咱們的 thunk
const thunk = ({ dispatch, getState }) => next => action => {
// 這段代碼,和咱們上面暴力修改 dispath 的方法,實際上是同樣的
if (typeof action === 'function') {
return action(dispatch, getState);
}
return next(action);
};
複製代碼
以上就是 redux 以及 redux-thunk 的簡單實現,有很多 bug,但願對你們理解 redux 有點幫助;也建議你們去看看 redux 的源碼,其實源碼也很短,而且註釋很完整。數據結構