createStore
是使用redux
的入口,也是使用redux
必須用到的一個方法,其顧名思義就是建立一個可供全局使用的store
react
該方法接受三個參數(reducer, preloadedState, enhance)
json
前兩個參數沒必要多說,第三個參數則是用來添加中間件,他是以原始的createStore
爲參數,而createStore
真正須要的數據爲reducer
和preloadedState
。redux
createStore.js
裏有這麼一行代碼:數組
if (typeof enhancer !== 'undefined') {
if (typeof enhancer !== 'function') {
throw new Error('Expected the enhancer to be a function.')
}
return enhancer(createStore)(reducer, preloadedState)
}
複製代碼
行代碼展現了enhancer的調用過程,根據這個調用過程咱們能夠推導出enhancer的函數體的架子應該是這樣子的:bash
function enhancer(createStore) {
return (reducer,preloadedState) => {
//邏輯代碼
.......
}
}
在實際應用中,enhancer每每就是redux-thunk以及redux-saga,通常都會配合applyMiddleware一塊兒使用,而applyMiddleware的做用就是將這些enhancer格式化成符合redux要求的enhancer。
複製代碼
createStore返回的是一個對象,咱們一般稱之爲store,store的結構以下:閉包
return {
dispatch,
subscribe,
getState,
replaceReducer,
[$$observable]: observable
}
複製代碼
createStore
的返回值爲一個對象,咱們經常使用的爲前三個方法即dispatch
, subscribe
, getState
這三種方法即服務於將數據流以flux的形式進行傳輸,即在對store的change進行監聽,並以訂閱發佈模式執行每一個監聽函數,固然redux在每一個階段都作了異常檢測。app
dispatch
對action
的作了isPlainObject
和undefined
的判斷,並在dispatching
的時候用一個flag–isDispatching
來標誌狀態,保證在同一時段只有一個dispatch
。函數
subscribe
接受listener
爲參數,此處的listener
是什麼?該函數首先肯定listener
爲function
,而後對isDispatching
作了判斷,確保store
的穩定性,而後以一個flag -- isSubscribed
來標誌該listener
的監聽狀態,同時對currentListeners
作了次淺拷貝後將 listener
添加到監聽隊列裏,此時完成了對監聽事件的綁定,以後返回一個該listener
取消訂閱的函數,該函數裏面即把 isSubscribed
置位,並又對currentListeners
作了次淺拷貝後,將 listener
從監聽隊列裏移除。ui
這裏每次在改變監聽隊列前都對監聽隊列作了一次淺拷貝的緣由是在於防止當redux在通知全部訂閱者的時候,此時又有一個新的訂閱者加進來了。若是隻用 currentListeners 的話,當新的訂閱者插進來的時候,就會打亂原有的順序,從而引起一些嚴重的問題。this
getState是redux暴露store的惟一方法,而後該方法內部也只對 isDispatching 作了判斷,避免在 store 改變時被獲取,而後直接返回store,可是不能直接對state賦值,而是經過setState的方法改變state,用來觸發訂閱者的監聽函數。
combineReducers
函數來把多個reducer
函數合併成一個reducer
函數。
其參數爲一個json對象及全部reducer
的一個集合,key爲store該reducer對應的屬性,值爲對應的reducer(一個函數)
返回值爲一個合併後的reducer函數
這個函數作了三件事:
1.對參數和參數的key(對應store中的屬性)作了一個淺拷貝
2.對淺拷貝後的參數中每一個reducer作返回值檢測(檢測是否有默認返回值)
3.返回一個合併後的reducer函數,而在返回的函數內部,combineReducers對其也作了異常檢測。異常檢測主要是由 getUnexpectedStateShapeWarningMessage 該方法執行,
getUnexpectedStateShapeWarningMessage
接收四個參數 inputState(state)
、reducers(finalReducers
)、action(action)
、unexpectedKeyCache(unexpectedKeyCache)
,這裏要說一下unexpectedKeyCache
是上一次檢測inputState
獲得的其裏面沒有對應的reducer
集合裏的異常key
的集合。整個邏輯以下:
再異常檢測定義了一個hasChanged變量用來表示state是否發生變化,遍歷reducers集合,將每一個reducer對應的原state傳入其中,得出其對應的新的state。
上面講enhance
的時候說過,applyMiddleware
返回的就是一個enhance
,applyMiddleware
顧名思義就是一個添加中間件的方法,而中間件添加的最佳時機在於dispatch
,由於 dispatch
就是store
改變的發起動做。
中間件極可能不止一個,所以寫成鏈式結構,能夠將其解耦,而第一個傳入的參數則爲dispatch
。---compose
下面是一個 applyMiddleware
的邏輯實現。
const applyMiddleware = function (...middlewares) { /*返回一個重寫createStore的方法*/ return function rewriteCreateStoreFunc(oldCreateStore) { /*返回重寫後新的 createStore*/ return function newCreateStore(reducer, initState) { /*1. 生成store*/ const store = oldCreateStore(reducer, initState); /*給每一個 middleware 傳下store,至關於 const logger = loggerMiddleware(store);*/ /* const chain = [exception, time, logger]*/ const chain = middlewares.map(middleware => middleware(store)); let dispatch = store.dispatch; /* 實現 exception(time((logger(dispatch))))*/ chain.reverse().map(middleware => { dispatch = middleware(dispatch); });
/2. 重寫 dispatch/ store.dispatch = dispatch; return store; } } }
applyMiddleware
主要就是經過鏈式增強dispatch
。實際的js代碼以下:
export default function applyMiddleware(...middlewares) {
return createStore => (...args) => {
const store = createStore(...args)
let dispatch = () => {
throw new Error(
`Dispatching while constructing your middleware is not allowed. ` +
`Other middleware would not be applied to this dispatch.`
)
}
const middlewareAPI = {
getState: store.getState,
dispatch: (...args) => dispatch(...args)
}
const chain = middlewares.map(middleware => middleware(middlewareAPI))
dispatch = compose(...chain)(store.dispatch)
return {
...store,
dispatch
}
}
}
複製代碼
整體的實現邏輯爲:
compose
的做用即將全部中間件以鏈式的形式調用。
bindActionCreators
不多用到,通常只有在 react-redux 的 connect
函數中的mapDispatchToProps
中用到。
其做用網上說是他經過閉包,把 dispatch
和actionCreator
隱藏起來,讓其餘地方感知不到 redux 的存在。可是我仍是不知道這樣作的意義是在於?
bindActionCreators
內部針對於三種狀況有三種返回值。
export default function bindActionCreators(actionCreators, dispatch) {
if (typeof actionCreators === 'function') {
return bindActionCreator(actionCreators, dispatch)
}
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"?`
)
}
const keys = Object.keys(actionCreators)
const boundActionCreators = {}
for (let i = 0; i < keys.length; i++) {
const key = keys[i]
const actionCreator = actionCreators[key]
if (typeof actionCreator === 'function') {
boundActionCreators[key] = bindActionCreator(actionCreator, dispatch)
}
}
return boundActionCreators
}
複製代碼
第一種狀況,當 typeof actionCreators === 'function'
時,
直接返回
return function() {
return dispatch(actionCreator.apply(this, arguments))
}
複製代碼
即直接返回一個返回值爲 dispatch
某個action
的函數的函數。
第二種狀況,當typeof actionCreators !== 'object' || actionCreators === null
時
一直拋出一個 Error ,指出bindActionCreators
須要的 actionCreator
是一個函數或者對象。
第三種狀況,即默認狀況,這個時候的 actionCreator 是不少個第一種狀況的集合,即 import * as manyActions from '../actions'
,這裏的 manyActions
就是做爲第三種狀況傳入 boundActionCreators
的 actionCreators
。
這個時候會對actionCreators
作一個遍歷,將每一項都執行一遍第一種狀況的操做。