解析:讓你弄懂redux原理


作者: HerryLo

本文永久有效鏈接: https://github.com/AttemptWeb......

Redux是JavaScript狀態容器,提供可預測化的狀態管理。

在實際開發中,常搭配React + React-redux使用。這代表了目前前端開發的一個基本理念,數據和視圖的分離。

redux應運而生,當然還有其他的一些狀態管理庫,如Flux、Elm等,當然,我們這里只對redux進行解析。

#redux創建Store

創建redux的store對象,需要調用combineReducers和createStore函數,下面解釋不包含中間件。

const reducer = combineReducers({ home: homeNumber, number: addNumber }) const store = createStore(reducer) // 暫時掛載在window下,下面會使用到 window.$reduxStore = store 

#combineReducers函數

首先調用combineReducers函數,將多個reducer函數作為參數傳入,源碼如下:

// reducers即是傳入的參數對象 function combineReducers(reducers) { // ......省略 return function combination(state = {}, action) { let hasChanged = false const nextState = {} for (let i = 0; i < finalReducerKeys.length; i++) { // finalReducerKeys 是傳入reducers對象的key值 const key = finalReducerKeys[i] // finalReducers 等價於 reducers const reducer = finalReducers[key] const previousStateForKey = state[key] // 運行reducer函數,返回一個state // 核心:調用combination函數,實際就是循環調用傳入的reducer函數 const nextStateForKey = reducer(previousStateForKey, action) nextState[key] = nextStateForKey // hasChanged = hasChanged || nextStateForKey !== previousStateForKey } // hasChanged = hasChanged || finalReducerKeys.length !== Object.keys(state).length // 返回state對象 return nextState } } // 源碼地址:https://github.com/reduxjs/redux/blob/master/src/combineReducers.ts#L139 

上面的代碼其實非常簡單,combineReducers函數運行,返回一個新的combination函數。combination函數的主要作用是返回一個掛載全部state的對象。 當combination函數被調用時,實際就是循環調用傳入的reducer函數,返回state對象。將combination函數作為參數傳入到createStore函數中。

#createStore函數

function createStore(reducer, preloadedState, enhancer) { // reducer --> combination函數 let currentReducer = reducer // 全部的state屬性,掛載在currentState上 let currentState = preloadedState // 下面的中間件會用到 if (typeof preloadedState === 'function' && typeof enhancer === 'undefined') { // 第二個參數是一個函數,沒有第三個參數的情況 enhancer = preloadedState // 將preloadedState重置 preloadedState = undefined } if (typeof enhancer !== 'undefined') { // 存在中間件時,將createStore傳入中間件函數,調用enhancer函數,return結束。 return enhancer(createStore)(reducer, preloadedState) } function dispatch(action) { // currentReducer --> combination函數 currentState = currentReducer(currentState, action) } // 初始化調用dispatch,創建初始state dispatch({ type: ActionTypes.INIT }) const store = ({ dispatch: dispatch, subscribe,s getState, replaceReducer, [$$observable]: observable } return store } // 源碼地址:https://github.com/reduxjs/redux/blob/master/src/createStore.ts#L60 

reducer就是傳入的combination函數,preloadedState是初始化的state(沒有太大的作用),enhancer是中間件,沒有第三個參數enhancer的情況下,同時第二個參數preloadedState是一個函數,preloadedState被賦值給enhancer。

調用dispatch函數初始化,currentReducer即是傳入combination函數,就向上文提到的,調用combination函數實際就是循環調用reducer函數。所有的state對象,被掛載在內部變量currentState上。存在中間件enhancer時,將createStore傳入中間件函數,調用enhancer函數,return結束,這個下文會繼續講到。

創建的store對象,暴露出的方法如下:

const store = ({ // 分發 action,這是觸發 state 變化的惟一途徑。 dispatch: dispatch as Dispatch<A>, // 變化監聽器 subscribe, // 獲取store下的 全部state getState, // 替換 store 當前用來計算 state 的 reducer replaceReducer } return store 

dispatch函數觸發action,調用reducer函數,修改state。subscribe函數可以監聽變化state的變化。getState函數獲取全部state。replaceReducer函數替換用來計算state的reducer函數。

通過combineReducers函數合並reducer函數,返回一個新的函數combination(這個函數負責循環遍歷運行reducer函數,返回全部state)。將這個新函數作為參數傳入createStore函數,函數內部通過dispatch,初始化運行傳入的combination,state生成,返回store對象

#redux中間件

最好把上面看懂之后,再看中間件部分!!下面對中間件進行分析:

redux-thunk只是redux中間件的一種,也是比較常見的中間件。redux-thunk庫允許你編寫與store交互的異步邏輯。

import thunkMiddleware from 'redux-thunk' const reducer = combineReducers({ home: homeNumber, number: addNumber }) const store = createStore( reducer, applyMiddleware( thunkMiddleware, // 異步支持 ) ) 

createStore函數支持三個參數,如果第二個參數preloadedState是一個函數,而沒有第三個參數enhancer的話,preloadedState會被賦值給enhancer。

下面會以redux-thunk中間件作為例子,下面就是thunkMiddleware函數的代碼:

// 部分轉為ES5代碼,運行middleware函數會返回一個新的函數,如下: return ({ dispatch, getState }) => { // next實際就是傳入的dispatch return function (next) { return function (action) { // redux-thunk核心 if (typeof action === 'function') { return action(dispatch, getState, extraArgument); } return next(action); }; }; } // 源碼地址:https://github.com/reduxjs/redux-thunk/blob/master/src/index.js 

redux-thunk庫內部源碼非常的簡單,github: redux-thunk 源碼,允許action是一個函數,同時支持參數傳遞,否則調用方法不變。

#applyMiddleware函數

// 中間件調用 return enhancer(createStore)(reducer, preloadedState) 等價於 return applyMiddleware( thunkMiddleware, )(createStore)(reducer, preloadedState) 

redux的中間件,從applyMiddleware函數開始,它主要的目的就是為了處理store的dispatch函數

// 支持多個中間件傳入 export default function applyMiddleware(...middlewares) { return (createStore) => (reducer, ...args) => { // 創建 store const store = createStore(reducer, ...args) const middlewareAPI = { getState: store.getState, dispatch: (action, ...args) => dispatch(action, ...args) } // 遍歷運行中間件函數,將middlewareAPI作為參數傳入 // middleware對應上面的redux-thunk庫核心代碼,當然也支持多個中間件 const chain = middlewares.map(middleware => middleware(middlewareAPI)) // 核心:將所有中間件傳入到compose中,返回一個新的dispatch dispatch = compose(...chain)(store.dispatch) // 照常返回一個store對象,dispatch已經被處理過了 return { ...store, dispatch } } } // 源碼地址:https://github.com/reduxjs/redux/blob/master/src/applyMiddleware.ts#L55 

applyMiddleware函數接收多個middlewares參數,返回一個store對象。通過createStore創建store對象,middlewareAPI對象掛載getState和dispatch,循環middlewares中間件,將middlewareAPI作為參數傳入每個中間件。遍歷結束以后,拿到了一個包含所有中間件新返回函數的一個數組,將其賦值給變量chain。

// 遍歷之后chain的值,這里只是拿redux-thunk庫作為例子 // next 就是 dispatch chain = [function (next) { return function (action) { if (typeof action === 'function') { // redux-thunk核心 return action(dispatch, getState, extraArgument); } return next(action); }; }, ...更多中間件] 

#compose函數

// 數組chain 保存所有中間件新返回函數 dispatch = compose(...chain)(store.dispatch) 

compose的主要作用就是運行所有中間件函數后,返回一個經過處理的dispatch函數。

// compose函數 return chain.reduce((a, b) =>{ return (...args)=> { return a(b(...args)) } } 

chain是保存中間件函數的數組,具體的內部結構參見上面👆,下面來分析一下compose函數的調用邏輯。

// chain 類比為 [fn1, fn2, fn3, fn4] [fn1, fn2, fn3, fn4].reduce((a, b) =>{ return (...args)=> { return a(b(...args)) } } 

調用過程如下:

循環 a值 b值 返回的值
第一輪循環 fn1 fn2 (...args)=> fn1(fn2(...args))
第二輪循環 (...args)=> fn1(fn2(...args)) fn3 (...args)=> fn1(fn2(fn3(...args)))
第三輪循環 (...args)=> fn1(fn2(fn3(...args))) fn4 (...args)=> fn1(fn2(fn3(fn4(...args))))

經過 compose 處理過之后, 最后的返回值就是 (...args) => fn1(fn2(fn3(fn4(...args)))),這個的arg就是 store.dispatch函數。最后將返回函數賦值給dispatch,就是我們需要的dispatch函數了。而如果只有一個中間件的話,就會直接返回了。

applyMiddleware函數中間件的主要目的就是修改dispatch函數,返回經過中間件處理的新的dispatch函數

#redux使用

這里的使用是不配合react-redux+react的;

window.$reduxStore = store store.dispatch(action); let { aState } = store.getState() 

上面是直接將其掛載在window對象之下,這樣可以配合任何前端框架使用,當然這樣肯定是不優雅的,后面我再會專門講一篇配合react-redux使用的;

在這里調用store.dispatch函數,實際就是再次調用循環遍歷調用reducer函數,更新之后被保存在createStore函數的內部變量currentState上。通過store.getState函數,返回currentState變量,即可得到所有state。

#結束:

內容有點多,需要總結一下

1.redux創建Store:通過combineReducers函數合並reducer函數,返回一個新的函數combination(這個函數負責循環遍歷運行reducer函數,返回全部state)。將這個新函數作為參數傳入createStore函數,函數內部通過dispatch,初始化運行傳入的combination,state生成,返回store對象。

2.redux中間件:applyMiddleware函數中間件的主要目的就是修改dispatch函數,返回經過中間件處理的新的dispatch函數

3.redux使用:實際就是再次調用循環遍歷調用reducer函數,更新state

解析到這里就結束了,redux真的非常函數化,滿滿的函數式編程的思想,非常的模塊化,具有很強的通用性,覺得非常贊👍👍

ps: 微信公眾號:Yopai,有興趣的可以關注,每周不定期更新,分享可以增加世界的快樂


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM