史上最全的 Redux 源碼分析
前言
用 React + Redux 已經一段時間了,記得剛開始用Redux 的時候感覺非常繞,總搞不起里面的關系,如果大家用一段時間Redux又看了它的源碼話,對你的理解會有很大的幫助。看完后,在回來看Redux,有一種 柳暗花明又一村 的感覺 .
源碼
我分析的是用 es6 語法的源碼,大家看目錄結構,一共有 6 個問件。先說下各個文件大概功能。
- applyMiddlewar.js 使用自定義的 middleware 來擴展 Redux
- bindActionCreators.js 把 action creators 轉成擁有同名 keys 的對象,使用時可以直接調用
- combineReducers.js 一個比較大的應用,需要對 reducer 函數 進行拆分,拆分后的每一塊獨立負責管理 state 的一部分
- compose.js 從右到左來組合多個函數,函數編程中常用到
- createStore.js 創建一個 Redux Store 來放所有的state
- utils/warnimng.js 控制臺輸出一個警告,我們可以不用看
我會按每個模塊的重要性,去做分析,今天就先把 createStore .js 分享給大家.
createStore.js
注釋很詳細,直接看注釋就可以了
// 導入 lodash ,判斷是否是普通(plain)對象
import isPlainObject from 'lodash/isPlainObject'
//導入 symbol 類型的 observable (symbol類型的屬性,是對象的私有屬性)
import $$observable from 'symbol-observable'
/*
定義 Redux Action 的初始化 type
- */
export var ActionTypes = {
INIT: '@@redux/INIT'
}
/**
- 創建一個Redux store來存放應用所有的 state。應用中有且只有一個store
*
- @param {Function} reducer 是一個函數,接收兩個參數,分別是當前的 state 樹和
- 要處理的 action,返回新的 state 樹
*
- @param {any} 初始化時的state ,在應用中,你可以把服務端傳來經過處理后的 state
傳給它。如果你使用 combineReducers 創建 reducer,它必須是一個普通對象,與傳入的 keys 保持同樣的結構。否則,你可以自由傳入任何 reducer 可理解的內容。
*
- @param {Function} enhancer 是一個組合的高階函數,返回一個強化過的 store creator .
- 這與 middleware相似,它也允許你通過復合函數改變 store 接口。
*
@returns {Store} 返回一個對象,給外部提供 dispatch, getState, subscribe, replaceReducer,
*/
export default function createStore(reducer, preloadedState, enhancer) {
//判斷 preloadedState 是一個函數并且 enhancer 是未定義
if (typeof preloadedState === 'function' && typeof enhancer === 'undefined') {
enhancer = preloadedState // 把 preloadedState 賦值給 enhancer
preloadedState = undefined // 把 preloadedState 賦值為 undefined
}
//判斷 enhancer 不是 undefined
if (typeof enhancer !== 'undefined') {
//判斷 enhancer 不是一個函數
if (typeof enhancer !== 'function') {
//拋出一個異常 (enhancer 必須是一個函數)
throw new Error('Expected the enhancer to be a function.')
}
//調用 enhancer ,返回一個新強化過的 store creator
return enhancer(createStore)(reducer, preloadedState)
}
//判斷 reducer 不是一個函數
if (typeof reducer !== 'function') {
//拋出一個異常 (reducer 必須是一個函數)
throw new Error('Expected the reducer to be a function.')
}
var currentReducer = reducer //把 reducer 賦值給 currentReducer
var currentState = preloadedState //把 preloadedState 賦值給 currentState
var currentListeners = [] //初始化 listeners 數組
var nextListeners = currentListeners//nextListeners 和 currentListeners 指向同一個引用
var isDispatching = false //標記正在進行dispatch
/**
- 保存一份訂閱快照
@return {void}
*/
function ensureCanMutateNextListeners() {
//判斷 nextListeners 和 currentListeners 是同一個引用
if (nextListeners === currentListeners) {
//通過數組的 slice 方法,復制出一個 listeners ,賦值給 nextListeners
nextListeners = currentListeners.slice()
}
}
/**
- 獲取 store 里的當前 state tree
*
@returns {any} 返回應用中當前的state tree
*/
function getState() {
//當前的state tree
return currentState
}
/*
- 添加一個 listener . 當 dispatch action 的時候執行,這時 sate 已經發生了一些變化,
- 你可以在 listener 函數調用 getState() 方法獲取當前的 state
*
- 你可以在 listener 改變的時候調用 dispatch ,要注意
*
- 訂閱器(subscriptions) 在每次 dispatch() 調用之前都會保存一份快照。
- 當你在正在調用監聽器(listener)的時候訂閱(subscribe)或者去掉訂閱(unsubscribe),
- 對當前的 dispatch() 不會有任何影響。但是對于下一次的 dispatch(),無論嵌套與否,
- 都會使用訂閱列表里最近的一次快照。
*
- 訂閱器不應該關注所有 state 的變化,在訂閱器被調用之前,往往由于嵌套的 dispatch()
- 導致 state 發生多次的改變,我們應該保證所有的監聽都注冊在 dispath() 之前。
*
- @param {Function} 要監聽的函數
@returns {Function} 一個可以移除監聽的函數
*/
function subscribe(listener) {
//判斷 listener 不是一個函數
if (typeof listener !== 'function') {
//拋出一個異常 (listener 必須是一個函數)
throw new Error('Expected listener to be a function.')
}
//標記有訂閱的 listener
var isSubscribed = true
//保存一份快照
ensureCanMutateNextListeners()
//添加一個訂閱函數
nextListeners.push(listener)
//返回一個取消訂閱的函數
return function unsubscribe() {
//判斷沒有訂閱一個 listener
if (!isSubscribed) {
//調用 unsubscribe 方法的時候,直接 return
return
}
//標記現在沒有一個訂閱的 listener
isSubscribed = false
//保存一下訂閱快照
ensureCanMutateNextListeners()
//找到當前的 listener
var index = nextListeners.indexOf(listener)
//移除當前的 listener
nextListeners.splice(index, 1)
}
}
/**
- dispath action。這是觸發 state 變化的惟一途徑。
- @param {Object} 一個普通(plain)的對象,對象當中必須有 type 屬性
*
- @returns {Object} 返回 dispatch 的 action
*
- 注意: 如果你要用自定義的中間件, 它可能包裝
dispatch()
返回一些其它東西,如( Promise )
*/
function dispatch(action) {
//判斷 action 不是普通對象。也就是說該對象由 Object 構造函數創建
if (!isPlainObject(action)) {
//拋出一個異常(actions 必須是一個普通對象. 或者用自定義的中間件來處理異步 actions)
throw new Error(
'Actions must be plain objects. ' +
'Use custom middleware for async actions.'
)
}
// 判斷 action 對象的 type 屬性等于 undefined
if (typeof action.type === 'undefined') {
//拋出一個異常
throw new Error(
'Actions may not have an undefined "type" property. ' +
'Have you misspelled a constant?'
)
}
//判斷 dispahch 正在運行,Reducer在處理的時候又要執行 dispatch
if (isDispatching) {
//拋出一個異常(Reducer在處理的時候不能 dispatch action)
throw new Error('Reducers may not dispatch actions.')
}
try {
//標記 dispatch 正在運行
isDispatching = true
//執行當前 Reducer 函數返回新的 state
currentState = currentReducer(currentState, action)
} finally { // (try catch) 最終會被執行的地方
//標記 dispatch 沒有在運行
isDispatching = false
}
//所有的的監聽函數賦值給 listeners
var listeners = currentListeners = nextListeners
//遍歷所有的監聽函數
for (var i = 0; i < listeners.length; i++) {
// 執行每一個監聽函數
listenersi
}
//返回傳入的 action 對象
return action
}
/**
- 替換計算 state 的 reducer。
*
- 這是一個高級 API。
- 只有在你需要實現代碼分隔,而且需要立即加載一些 reducer 的時候才可能會用到它。
- 在實現 Redux 熱加載機制的時候也可能會用到。
*
- @param {Function} store 要用的下一個 reducer.
@returns {void}
*/
function replaceReducer(nextReducer) {
//判斷 nextReducer 不是一個函數
if (typeof nextReducer !== 'function') {
//拋出一個異常 (nextReducer必須是一個函數)
throw new Error('Expected the nextReducer to be a function.')
}
//當前傳入的 nextReducer 賦值給 currentReducer
currentReducer = nextReducer
//調用 dispatch 函數,傳入默認的 action
dispatch({ type: ActionTypes.INIT })
}
/**
- 在 creatorStore 內部沒有看到此方法的調用
- (猜想 : 作者可能想用比較強大,活躍的 observable 庫替換現在的 publish subscribe)
*
- @returns {observable} 狀態改變的時候返回最小的 observable.
- 想要了解跟多關于 observable 庫,建議看下
https://github.com/zenparsing/es-observable (標準 es Observable)
*/
function observable() {
//訂閱方法賦值給變量 outerSubscribe
var outerSubscribe = subscribe
return {
/**
- 這是一個最小的觀察訂閱方法
- @param {Object} 觀察著的任何一個對象都可以作為一個 observer.
觀察者應該有 next
方法
*/
subscribe(observer) {
//判斷 observer 是一個對象
if (typeof observer !== 'object') {
//拋出異常
throw new TypeError('Expected the observer to be an object.')
}
//獲取觀察著的狀態
function observeState() {
if (observer.next) {
observer.next(getState())
}
}
observeState()
//返回一個取消訂閱的方法
var unsubscribe = outerSubscribe(observeState)
return { unsubscribe }
},
//對象的私有屬性,暫時不知道有什么用途
$$observable {
return this
}
}
}
//reducer 返回其初始狀態
//初始化 store 里的 state tree
dispatch({ type: ActionTypes.INIT })
//返回 store 暴漏出的接口
return {
dispatch, //唯一一個可以改變 state 的哈按時
subscribe, //訂閱一個狀態改變后,要觸發的監聽函數
getState, // 獲取 store 里的 state
replaceReducer, //Redux熱加載的時候可以替換 Reducer
[$$observable]: observable //對象的私有屬性,供內部使用
}
}</code></pre>
來自:http://www.cnblogs.com/qiaojie/p/6144454.html