從大學到如今用 React
開發差很少有1年多了,雖然仍是一個小菜鳥,不過 react
虐我千百遍,我仍待她如初戀嘻嘻。html
Redux
是咱使用的第一個狀態管理器,在學習的時候總以爲它是依賴於 react
的,還和 react-redux
搞混了。瞭解了官網以及查閱不少關於 redux
才知道其實 redux
和 react
以及 react-redux
沒有什麼關係,由於它是原生 js 編寫的,因此任何框架均可以使用它。而react-redux
爲react
和redux
提供了更爲便捷的關聯方式,方便咱們維護;react
開始進入咱們的真香環節,若是有不怎麼熟悉 redux
建議先閱讀中文文檔redux
createStore 函數用於建立而且返回一個狀態操做對象,如下是對 store 對象的部分類型定義;api
interface action { type: string; [payload: string]: any; } type subscribe = (lister: Function) => Function interface Store { dispatch: action; getState: Function; subscribe: subscribe; }
store 對象主要有有getState
,dispatch
和subscribe
這幾個 API。數組
getState
用於獲取最新的狀態dispatch
用於派發action
對象進行狀態更新subscribe
用於監聽狀態變動知道這幾個 API 的功能後咱們之一來實現吧~安全
/** * createStore * 建立 store 對象,並將對象返回 * @param {(state:{[key:string]:any},action:{type: string,[key:string]:any}) => {[key:string]:any}} reducer * @param {Function} [middleware] 中間件 * @returns {{dispatch: Function,getState: Function,subscribe: Function}} state 操做對象 */ function createStore(reducer) { // store 狀態 let state; //監聽隊列 let listers = []; /** * 獲取最新的 state * @returns {store} state */ function getState() { const { parse, stringify } = JSON; // 爲了安全起見,返回深拷貝後的 state 對象,防止組件隨意增長屬性形成數據污染 return parse(stringify(state)); } /** * 發佈函數 * 接受一個 action 對象 * @param {{type: string,[key:string]:any}} action * @returns {{[key:string]: any}} action */ function dispatch(action) { // 將 action 派發給 reducer 函數進行狀態處理,而且更新最新的 state state = reducer(state, action); // 狀態更新後還得執行如下咱們的監聽隊列,告訴他們咱們的 state 更新啦 listers.forEach(observer => typeof observer === 'function' && observer()); // 將這次分發的 action 返回出去 return action; } /** * 訂閱函數 * @param {Function} lister 監聽函數 * @returns {Function} disconnect 註銷監聽 */ function subscribe(lister) { if (typeof lister !== 'function') { console.warn( 'The Lister parameter of the subscribe function must be a function' ); // 返回一個匿名函數,防止報錯 return () => { // 順便在多提示幾下  ̄ω ̄= console.warn( 'The Lister parameter of the subscribe function must be a function' ); }; } return function () { // 將監聽的數組從 listers (監聽隊列)移除掉 listers = listers.filter(observer => observer !== lister); }; } // 初始化 state ,派發一個私有的 action,避免重名影響到狀態誤改 dispatch({ type: `CODE_ROOKIE_262@@${Date.now().toString(16)}` }); return { dispatch, getState, subscribe }; }
以上基本實現了 createStore
。app
基本用法
// 定義一個 計數器 reducer function reducer(state,action){ switch(action.type){ case 'add': return {...state,count: state.count + 1}; case 'minus': return {...state,count: state.count - 1}; default: return state; } } // 建立 Store const store = createStore(reducer); // 訂閱狀態監聽 let observer = store.subscribe(function(){ console.log('new state',store.getState()) }); // 派發 action 更改狀態 store.dispatch({ type: 'add' }) // 註銷監聽 observer()
有的時候咱們的狀態是有模塊區分的,就和 Vuex
同樣有多個 module
進行分開管理,避免多個狀態一同處於同一級形成數據會形成難以維護,因此咱們也須要對不一樣的狀態進行劃分,須要將reducer
函數拆分紅多個單獨的函數對獨自的狀態狀態管控。可是上文中的 createStore
只接受一個 reducer
,如何將多個 reducer
同時傳進入呢?框架
這個時候就是咱們大哥 combineReducers
的工做了,他接受一個對象,經過{reducer: reducerFun}
形式的參數傳遞給 combineReducers
,combineReducers
接收到參數後會返回一個reducer
函數,將這個函數傳遞給 createStore
便可。函數
/** * 合併多個 reducer 函數 * @param reducers {reducer1: reducer1,reducer2: reducer2,...} * @returns reducer */ function combineReducers(reducers) { return function reducer(state = {}, action) { let newState = {}; // 更新每一個模塊的 state,而且將其最新狀態返回 for (var key in reducers) { newState[key] = reducers[key](state[key], action); } return newState; }; }
使用方法
const count = function countReducer(state,action){...}; const list = function countReducer(state,action){...}; const reducer = combineReducers({ count: count, list: list }) const store = createStore(reducer);
使用包含自定義功能的 middleware 來擴展 Redux 是一種推薦的方式。Middleware 可讓你包裝 store 的 dispatch 方法來達到你想要的目的。同時, middleware 還擁有「可組合」這一關鍵特性。多個 middleware 能夠被組合到一塊兒使用,造成 middleware 鏈。其中,每一個 middleware 都不須要關心鏈中它先後的 middleware 的任何信息。
以上是引用 redux 中文網學習
能夠看出 applyMiddleware
主要是經過 一個或者多個 middleware
包裝 store.dispatch
。
中間件的用法
// redux-logger 是打印狀態變動的一箇中間件 import logger from 'redux-logger'; import {createStore,applyMiddleware} from 'redux' function reducer(state,action){} let store = createStore(reducer,applyMiddleware(logger))
這個時候咱們得給個人上面的 createStore
函數進行一些參數的調整。
function createStore(reducer,middleware){ //...other code // 安裝 middleware if(typeof middleware === 'function'){ return middleware(createStore)(reducer) } return { getState: getState, dispatch: dispatch, subscribe: subscribe } }
咱們先從單箇中間件middleware
的思路入手更容易理解吧~
function applyMiddleware(middleware){ return function(createStore){ return function(reducer){ // 建立 store let store = createStore(reducer); // 將 store 傳入中間件 let middle = middleware(store); // 將 dispatch 傳入中間件返回的函數進行修飾,並返回 let middleDispatch = middle(store.dispatch); return { ...store, dispatch: middleDispatch } } } }
基本的中間件注入是解決了,可是applyMiddleware
是支持多箇中間件的,也就是說最後返回的dispatch
只有一個,而且是通過了多箇中間件修飾過的了。那麼就有一個難點,就是如何獲取最終的的dispatch
呢?
例若有 middleware1
和middleware2
,每一個函數都接受原有的dispatch
後返回新的dispatch
,
能夠分解爲:
const dispatch1 = middleware1(dispatch); const dispatch2 = middleware(dispatch11);
因此咱們是否是能夠這樣寫呢 => middleware2(middleware2(dispatch))
;
那咱們能夠封裝一函數 compose
將多個 middleware
組合成一個函數,這個函數接受一個的 dispatch
並返回最終的 dispatch
;
在此以前咱們能夠看下 redux
源碼中的處理,咱們能夠在 compose.js
中看到這樣一句代碼,很是巧妙。
funcs.reduce((a, b) => (...args) => a(b(...args)));
能夠看看簡化後的代碼
function compose(...funs) { return arg => { let res = arg; for (var i = 0, fl = funs.length; i < fl; i++) { // 接受返回值,而且將上一個函數的返回值傳遞給當前函數 res = funs[i](res); } return res; }; }
看完後是否是煥然大悟了哈哈哈,固然用 數組的 reduce
還有另外的寫法喲´( ̄▽ ̄)~*
function compose(...funs) { return dispatch => funs.reduce((res, fun) => fun(res), dispatch); }
關於插件合併的問題咱們基本解決了,接下來咱們再次修改一下咱們的 applyMiddleware
函數。
function applyMiddleware(...middleware){ return function(createStore){ return function(reducer){ // 建立 store let store = createStore(reducer); // 將 store 傳入中間件 + let middles = middleware.map(middle => middle(store)); // 將 dispatch 傳入中間件返回的函數進行修飾,並返回 + let middleDispatch = compose(...middles)(store.dispatch); return { ...store, dispatch: middleDispatch } } } }
這樣就實現多個插件同時注入的問題了,也基本實現了redux
的功能,在實現的過程當中雖然會遇到不少挫折,不過最終實現完成更讓人充滿動力哈哈哈Y(^o^)Y