Redux API
export {
createStore, //創建一個state用來存儲狀態樹
combineReducers, //合併reducer
bindActionCreators, //將dispatch和action結合
applyMiddleware, //調度中間件來增強store,例如中間件redux-thunk等
compose //從右向左組合多個函數, compose(f, g, h)會返回(...args) => f(g(h(...args)))
}
源碼結構
Redux源碼結構和提供的API大體對應,如下:
utils——warning.js //console中打印warning信息要用到的
applyMiddleware.js
bindActionCreators.js
combineReducers.js
compose.js
createStore.js
index.js //export 上述定義的module
createStore
上面我們看到了redux的API和源碼結構,看的出來,warning.js和index.js不用解析,都看得懂,關鍵時其餘的幾個module,那我們從最重要的createStore講起。
export var ActionTypes = {
INIT: '@@redux/INIT'
}
//首先定義了一個action類型,我們知道更新state的唯一方法就是dispatch一個action,這個action是用
// 來初始化state的,後面會用到它
現在來看下createStore的大體結構
//接收三個參數
//reducer爲function。當dispatch一個action時,此函數接收action來更新state
//preloadState初始化State
//enhancer 爲function。用來增強store, Redux 定義有applyMiddleware來增強store,後面會
//單獨講applyMiddleware
export default function createStore(reducer, preloadedState, enhancer) {
if (typeof preloadedState === 'function' && typeof enhancer === 'undefined') {
//如果只傳了兩個參數,並且第二個參數爲函數,第二個參數會被當作enhancer
enhancer = preloadedState
preloadedState = undefined
}
if (typeof enhancer !== 'undefined') {
if (typeof enhancer !== 'function') {
//校驗enhancer是否爲函數,如果不是函數則拋出異常
throw new Error('Expected the enhancer to be a function.')
}
//如果enhancer存在且爲函數,那麼則返回如下調用,如果enhancer爲applyMiddleware,那麼調用則
//是applyMiddleware(createStore)(reducer, preloadedState)。後面講applyMiddleware再詳細講。
return enhancer(createStore)(reducer, preloadedState)
}
if (typeof reducer !== 'function') {
//校驗reducer是否爲函數
throw new Error('Expected the reducer to be a function.')
}
var currentReducer = reducer
//得到reducer
var currentState = preloadedState
//得到初始init,沒有傳遞則爲undefined
var currentListeners = []
//定義一個數組用來存放listeners。就是一個函數數組,當state發生改變時,會循環執行這個數組裏面的函數
var nextListeners = currentListeners
//用來存儲下一次的listeners數組。爲什麼要有這個listeners數組呢?因爲當state發生改變時,我們根據
//上面的currentListeners來循環執行函數,但是在這執行這些函數時,函數內部可能取消或者添加訂閱
//(state改變時,添加或者取消執行函數),這時如果直接操作currentListeners ,相當於在循環
//內部修改循環條件,執行瞬間就亂套了,有沒有啊,有沒有
var isDispatching = false
//reducer函數是否正在執行的標識
function ensureCanMutateNextListeners() {
//拷貝currentListeners一份爲nextListeners,這樣nextListeners的改變不會引起currentListeners的改變
//(上面解釋過原因啦)
}
function dispatch() {
//觸發action去執行reducer,更新state
.....
}
function subscribe() {
//接收一個函數參數,訂閱state的改變。當state改變時會執行這個函數
....
}
function getState() {
//獲取state樹
....
}
function replaceReducer() {
//替換reducer
....
}
function observable() {
//沒用,不解釋(後面有解釋)
......
}
dispatch({ type: ActionTypes.INIT })
//執行dispatch函數,初始化state
return {
//真正的返回,執行createStore其實返回的就是這些東東
dispatch, //觸發action去執行reducer,更新state
subscribe, //訂閱state改變,state改變時會執行subscribe的參數(自己定義的一個函數)
getState, //獲取state樹
replaceReducer, //替換reducer
[$$observable]: observable
//redux內部用的,對我們來說沒用(非要深究它寫這是幹嘛的?咋跟
//我一樣死腦筋呢,都說了沒用啦。算了,還是告訴你把,就是內部用的,在測試代碼中會用到,感興
//趣的可以去test目錄下查看)
}
}
現在是不是感覺明朗(懵逼)了許多,懵逼就對了,接下來我們再來解析一下dispatch, subscribe等函數的具體實現,或許會讓你明朗(更懵逼)起來
看了上面的大體結構,我們明白以下這些就夠了。
createStore是一個函數,它定義了一些變量(currentState, currentListeners等)及函數(dispatch, subscribe等),並且調用了dispatch,最後返回一個對象,該對象包含的就是dispatch和subscribe等函數。接下來我們來解析這些函數。
createStore裏面只調用了一個函數,那就是dispatch,那我們就從這裏開始講起。
dispatch
它是這樣被調用的,有沒有很熟悉。。。
dispatch({ type: ActionTypes.INIT })
來看看dispatch的源碼
function dispatch(action) {
//校驗action參數,必須爲一個純粹的對象。這也說明了,我們不能直接在redux做異步請求,而是需要
//使用applyMiddleware去應用一些中間件。比如redux-thunk等
if (!isPlainObject(action)) {
throw new Error(
'Actions must be plain objects. ' +
'Use custom middleware for async actions.'
)
}
//同樣是校驗參數,不解釋,相信都能看懂
if (typeof action.type === 'undefined') {
throw new Error(
'Actions may not have an undefined "type" property. ' +
'Have you misspelled a constant?'
)
}
//判斷是否正在執行reducers函數,如果正在執行,此action不會觸發,那有人就問題了,那是不是我
//的dispatch不會起作用,導致state沒有更新,數據是錯誤的? 答案是state是不會有錯。因爲redux本
//身整個更新state的過程是同步的,從dipatch——>reducers——>state。所以這段代碼意在你定義的
//reducers函數中不允許調用dispatch
if (isDispatching) {
throw new Error('Reducers may not dispatch actions.')
}
try {
//設置標識爲,並且執行currentReducer,還記得嗎?這個我們通過參數獲取到的一個函數,往往是我
//們調用combineReducers返回,combineReducers我們後面解析
isDispatching = true
//調用reducers函數
currentState = currentReducer(currentState, action)
} finally {
isDispatching = false
}
//調用所有訂閱state改變的函數,這些函數就可以通過getState函數獲取到最新的state值。訂閱的函數
//從哪裏來呢,從subscribe中來, 我們後面來解析subscribe和getState
var listeners = currentListeners = nextListeners
for (var i = 0; i < listeners.length; i++) {
listeners[i]()
}
return action
}