Redux是React核心開發(fā)人員的開發(fā)的一個JavaScript 狀態(tài)容器,提供可預(yù)測化的狀態(tài)管理。
Redux短小精悍并且沒有任何第三方的依賴。
源碼接口
.src
├── utils #工具函數(shù)
├── applyMiddleware.js
├── bindActionCreators.js
├── combineReducers.js
├── compose.js
├── createStore.js
└── index.js #入口 js
index.js
對外的整個代碼的入口燕侠,暴露了 createStore, combineReducers, bindActionCreators, applyMiddleware, compose幾個接口給開發(fā)者者祖。另外也會warn提示 判斷redux是否被壓縮。
createStore.js
redux中最重要的一個API了绢彤,它創(chuàng)建一個Redux store來存放應(yīng)用所有的state七问,整個應(yīng)用中有且只有一個store。
import isPlainObject from 'lodash/isPlainObject'
import $$observable from 'symbol-observable'
// 私有 action 僅供自己初始化使用
export var ActionTypes = {
INIT: '@@redux/INIT'
}
export default function createStore(reducer, preloadedState, enhancer) {
// 判斷接受的參數(shù)個數(shù)茫舶,來指定 reducer 械巡、 preloadedState 和 enhancer
if (typeof preloadedState === 'function' && typeof enhancer === 'undefined') {
enhancer = preloadedState
preloadedState = undefined
}
// 如果 enhancer 存在并且適合合法的函數(shù),那么調(diào)用 enhancer饶氏,并且終止當前函數(shù)執(zhí)行
if (typeof enhancer !== 'undefined') {
if (typeof enhancer !== 'function') {
throw new Error('Expected the enhancer to be a function.')
}
// 如果使用了第三方的庫:redux-thunk 則會進入這個流程 enhancer只接受 applyMiddleware函數(shù)返回
return enhancer(createStore)(reducer, preloadedState)
}
if (typeof reducer !== 'function') {
throw new Error('Expected the reducer to be a function.')
}
// 儲存當前的 currentReducer
var currentReducer = reducer
// 儲存當前的狀態(tài)
var currentState = preloadedState
// 儲存當前的監(jiān)聽函數(shù)列表
var currentListeners = []
// 儲存下一個監(jiān)聽函數(shù)列表
var nextListeners = currentListeners
var isDispatching = false
// 這個函數(shù)可以根據(jù)當前監(jiān)聽函數(shù)的列表生成新的下一個監(jiān)聽函數(shù)列表引用
function ensureCanMutateNextListeners() {
if (nextListeners === currentListeners) {
nextListeners = currentListeners.slice()
}
}
... getState ...
... subscribe ...
... dispatch ...
... replaceReducer ...
... observable ...
// 當store創(chuàng)建了讥耗,調(diào)用dispatch方法 傳遞type: ActionTypes.INIT
dispatch({ type: ActionTypes.INIT })
return {
dispatch,
subscribe,
getState,
replaceReducer,
[$$observable]: observable
}
}
函數(shù)對外暴露方法:dispatch, subscribe, getState等等方法。
-
dispatch
函數(shù)參數(shù)傳遞的是action疹启, action為一個普通的plain object對象葛账。
function dispatch(action) { if (!isPlainObject(action)) { throw new Error( 'Actions must be plain objects. ' + 'Use custom middleware for async actions.' ) } // 判斷 action 是否有 type{必須} 屬性 if (typeof action.type === 'undefined') { throw new Error( 'Actions may not have an undefined "type" property. ' + 'Have you misspelled a constant?' ) } // 如果正在 dispatch 則拋出錯誤 if (isDispatching) { throw new Error('Reducers may not dispatch actions.') } // 對拋出 error 的兼容,但是無論如何都會繼續(xù)執(zhí)行 isDispatching = false 的操作 try { isDispatching = true // 使用 currentReducer 來操作傳入 當前狀態(tài)和action皮仁,放回處理后的狀態(tài) currentState = currentReducer(currentState, action) } finally { isDispatching = false } var listeners = currentListeners = nextListeners for (var i = 0; i < listeners.length; i++) { var listener = listeners[i] listener() } return action }
傳遞dispatch(action) currentState = currentReducer(currentState, action) 傳入當前的state 和action 并返回一個新的state。
-
subscribe
可以給store的狀態(tài)添加訂閱監(jiān)聽函數(shù)菲宴,一旦調(diào)用 ' dispatch '贷祈,所有的監(jiān)聽函數(shù)就會執(zhí)行,本身存儲了一個擋墻監(jiān)聽函數(shù)的列表喝峦。 調(diào)用subcribe函數(shù)傳入一個函數(shù)作為參數(shù)势誊,同時會返回一個unsubscribe函數(shù),用來解綁當前傳入的監(jiān)聽函數(shù)谣蠢。
-
getState
返回當前store存儲的currentState
compose.js
函數(shù)式編程中的概念粟耻,接受一組函數(shù)參數(shù),從右到左來組合多個函數(shù)眉踱,然后返回一個組合函數(shù)
applyMiddleware.js
函數(shù)的作用是組合多個中間件等等挤忙,然后返回一個函數(shù)也就是所謂的enhancer。
export default function applyMiddleware(...middlewares) {
// args 也就是所謂的reducer, preloadedState, enhancer
// createStore也就是之前的createStore函數(shù) 只是加上了它基礎(chǔ)上加上了第三方的使用
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.`
)
}
let chain = []
// 暴露getState dispatch給第三方中間件使用
const middlewareAPI = {
getState: store.getState,
dispatch: (...args) => dispatch(...args)
}
chain = middlewares.map(middleware => middleware(middlewareAPI))
dispatch = compose(...chain)(store.dispatch)
return {
...store,
dispatch
}
}
}
這段代碼寫的很巧妙:在之前的createStore() 中
if (typeof enhancer !== 'undefined') {
if (typeof enhancer !== 'function') {
throw new Error('Expected the enhancer to be a function.')
}
return enhancer(createStore)(reducer, preloadedState)
}
所以如果有第三方庫谈喳,enhancer(createStore)(reducer, preloadedState)册烈,接著調(diào)用applyMiddleware,所以此處相當于調(diào)用了 var store = createStore(reducer, preloadedState, enhancer ) ; 接著會重新生成一個新的dispatch婿禽, 首先保存之前的store.dispatch赏僧。
var middlewareAPI = {
getState: store.getState,
dispatch: (action) => dispatch(action)
}
chain = middlewares.map(middleware => middleware(middlewareAPI))
dispatch = compose(...chain)(store.dispatch)
對所有的第三方中間件都暴露上面兩個方法, 同時改造dispatch扭倾,使用compose來使第三方中間件從右向左依次激活淀零。
CombineReducers
export default function combineReducers(reducers) {
const reducerKeys = Object.keys(reducers)
const finalReducers = {}
for (let i = 0; i < reducerKeys.length; i++) {
const key = reducerKeys[i]
//......
if (typeof reducers[key] === 'function') {
finalReducers[key] = reducers[key]
}
}
const finalReducerKeys = Object.keys(finalReducers)
return function combination(state = {}, action) {
// ...
let hasChanged = false
const nextState = {}
for (let i = 0; i < finalReducerKeys.length; i++) {
const key = finalReducerKeys[i]
const reducer = finalReducers[key]
const previousStateForKey = state[key]
const nextStateForKey = reducer(previousStateForKey, action)
// ...
nextState[key] = nextStateForKey
hasChanged = hasChanged || nextStateForKey !== previousStateForKey
}
return hasChanged ? nextState : state
}
}
首先根據(jù)key 生成一個新的finalReducers,finalReducerKeys膛壹,然后返回一個新的 combination函數(shù)()驾中,新的reducer函數(shù) 拿到之前的state狀態(tài)唉堪,然后接著調(diào)用reducer函數(shù)計算出接下來的state。
// 獲取之前的state的狀態(tài)
const previousStateForKey = state[key]
// 計算獲取action的接下來的狀態(tài)
const nextStateForKey = reducer(previousStateForKey, action)
通過hasChanged變量來判斷是否是返回一個新的nextState對象還是之前就的state 判斷的標準也就是所謂的 === 三個等號對引用值的判斷哀卫。