1.前言
關於redux的基本概念和工作流如何進行的這裏就不進行過多概述了,可以查看相關文檔去了解。
流程圖鏈接
2.redux源碼結構
以下是redux的源碼結構圖,主要的就是以下幾個文件組成,我們接下來按順序進行介紹其中原理和實現過程。
3.createStore.js
首先了解下createStore.js。通過調用createStore創建唯一的store,store中暴露出getState,dispatch,subscribe,replaceReducer這幾個方法。通常我們用到的主要是前三個方法,這裏作爲主要介紹內容。如下是createStore的主要內容:
export function createStore(reducer, preloadedState, enhancer) {
/**
* 以下的判斷都是對傳入的參數進行驗證
*/
if(
(typeof preloadedState === 'function' && typeof enhancer === 'function') ||
(typeof enhancer === 'function' && typeof arguments[3] === 'function')
) {
throw new Error('只能傳遞一個enhancer到createStore()中')
}
if(typeof preloadedState === 'function' && typeof enhancer === 'undefined') {
enhancer = preloadedState
preloadedState = undefined
}
if(typeof enhancer !== 'undefined') {
if(typeof enhancer !== 'function') {
throw new Error('enhancer應該爲一個函數')
}
return enhancer(createStore)(reducer, preloadedState)
}
if(typeof reducer !== 'function') {
throw new Error('reducer應該爲一個函數')
}
/**
* 初始化參數
*/
let currentReducer = reducer //初始化reducer
let currentState = preloadedState //初始化state
let currentListeners = [] //初始化subscribe監聽函數數組
let nextListeners = currentListeners
let isDispatching = false
/**
* 複製一份currentListeners,爲了防止在dispatch的時候
* 調用subscribe和unsubscribe時候發生錯誤
*/
function ensureCanMutateNextListeners() {
if(nextListeners === currentListeners) {
nextListeners = currentListeners.slice()
}
}
/**
* 獲取當前的state
*/
function getState() {
if(isDispatching) {
throw new Error('不可以在isDispatching的時候調用getState')
}
return currentState
}
/**
* 訂閱監聽事件,觸發dispatch後執行
*/
function subscribe(listener) {
if(typeof listener != 'function') {
throw new Error('Expected the listener to be a function.')
}
if(isDispatching) {
throw new Error('isDispatching的時候無法調用')
}
let isSubscribed = true
ensureCanMutateNextListeners()
nextListeners.push(listener)
return function unsubscribe() {
if(!isSubscribed) { //正在解除監聽事件的時候不向下執行
return
}
if(isDispatching) {
throw new Error('正在dispatch的時候不給執行')
}
isSubscribed = false
ensureCanMutateNextListeners()
const index = nextListeners.indexOf(listener)
nextListeners.splice(index)
}
}
/**
* 執行好dispatch循環調用每個subscribe的函數
*/
function dispatch() {
//關於驗證的代碼就不寫了
const listeners = (currentListeners = nextListeners)
for(let i=0; i<listeners.length; i++) {
listeners[i]()
}
return action
}
/**
* 替換當前的reducer然後重新初始化一次dispatch
*/
function replaceReducer(nextReducer) {
currentReducer = nextReducer
dispatch({type: '@INITACTION'})
}
//初始化執行dispatch
dispatch({type: '@INITACTION'})
}
4. combineReducers.js
combineReducers,它接收多個reducer函數,並整合,歸一化成一個rootReducer。其返回值rootReducer將會成爲createStore的參數,完成store的創建。
combineReducers只接收一個參數,這個參數闡述了不同reducer函數和頁面狀態數據樹不同部分的映射匹配關係。
const combineReducers = (reducers) => {
return (state={}, action) => {
Object.keys(reducers).reduce((nextState, key) => {
nextState[key] = reducers[key](state[key], action)
return nextState
}, {})
}
}
5. applyMiddleware.js
可以通過此方法給redux在觸發action到reducer的過程中增加一箇中間環節。applyMiddleware返回的內容我們稱爲enhancer。這個是createStore方法的最後一個參數,並且是可選的。
在redux源碼中涉及中間件的腳本有applyMiddleware.js、createStore.js、compose.js。那麼applyMiddleware(...middlewares)中會發生什麼事情。
在createStore.js中有一段源碼如下:
export default function createStore(reducer, preloadedState, enhancer) {
//...
return enhancer(createStore)(reducer, preloadedState)
//...
}
顧名思義,applyMiddleware就是對各個需要的中間件進行糅合,並作爲createStore的第二個或者第三個參數傳入。用於增強store。源碼如下:
const combineReducers = (reducers) => {
return (state = {}, action) => {
return Object.keys(reducers).reduce((nextState, key) => {
nextState[key] = reducers[key](state[key], action)
return nextState
}, {})
}
}
export default function applyMiddleware(...middlewares) {
return (next) => {
return (reducer, initialState) => {
var store = next(reducer, initialState)
var dispatch = store.dispatch
var chain = []
//包裝一下store的getState和dispatch方法
//是第三方中間件需要使用的參數
var middlewareAPI = {
getState: store.getState,
dispatch: (action) => dispatch(action)
}
//每個中間件也是一個高度柯里化的函數,它接收middlewareAPI參數後的第一次返回結果並存儲到chain數組中
//chain數組中每一項都是對dispatch的增強,並進行控制權轉移。
chain = middlewares.map(middleware => middleware(middlewareAPI))
//這裏的dispatch函數就是增強後的dispatch,因此compose方法接收了chain數組和原始dispatch方法。
dispatch = compose(...chain, store.dispatch)
return {
...store,
dispatch
}
}
}
}
export default function compose(...funcs) {
if(funcs.length === 0) {
return arg => arg
}
if(funcs.length === 1) {
return funcs[0]
}
return funcs.reduce((a, b) => (...args) => a(b(...args)))
}
6. compose.js
這個方法在applymiddleware中介紹了,可以在上面看到。
7.bindActionCreators.js
這個模塊涉及的內容較少,我們直接去看源碼:
function bindActionCreator(actionCreator, dispatch) {
//這個函數主要作用就是返回一個函數,當我們調用返回的這個函數的時候
//會自動的dispatch對應的action
return function() {
return dispatch(actionCreator.apply(this, args))
}
}
/**
參數說明:
actionCreators: action create函數,可以是一個單函數,也可以是一個對象,這個對象的所有元素都是action create函數
dispatch: store.dispatch方法
*/
export default function bindActionCreators(actionCreators, dispatch) {
// 如果actionCreators是一個函數的話,就調用bindActionCreator方法對action create函數和dispatch進行綁定
if (typeof actionCreators === 'function') {
return bindActionCreator(actionCreators, dispatch)
}
// actionCreators必須是函數或者對象中的一種,且不能是null
if (typeof actionCreators !== 'object' || actionCreators === null) {
throw new Error(
`bindActionCreators expected an object or a function, instead received ${actionCreators === null ? 'null' : typeof actionCreators}. ` +
`Did you write "import ActionCreators from" instead of "import * as ActionCreators from"?`
)
}
// 獲取所有action create函數的名字
const keys = Object.keys(actionCreators)
// 保存dispatch和action create函數進行綁定之後的集合
const boundActionCreators = {}
for (let i = 0; i < keys.length; i++) {
const key = keys[i]
const actionCreator = actionCreators[key]
// 排除值不是函數的action create
if (typeof actionCreator === 'function') {
// 進行綁定
boundActionCreators[key] = bindActionCreator(actionCreator, dispatch)
}
}
// 返回綁定之後的對象
/**
boundActionCreators的基本形式就是
{
actionCreator: function() {dispatch(actionCreator.apply(this, arguments))}
}
*/
return boundActionCreators
}