redux原理解析,看這篇就夠了

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 就是 dispatchchain = [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真的非常函數化,滿滿的函數式編程的思想,非常的模塊化,具有很強的通用性,覺得非常贊

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章