回顧一下 redux
的目錄結構:redux
.REDUXSRC
│ applyMiddleware.js
│ bindActionCreators.js
│ combineReducers.js
│ compose.js
│ createStore.js
│ index.js
│
└─utilssegmentfault
actionTypes.js isPlainObject.js warning.js
redux
在 index.js
中一共暴露了5個 API
, 上一篇文章講了下和 redux
關聯性不太大的 compose
。如今正式講一講最核心的 createStore
。api
createStore
大概是長成這個樣子的:數組
import $$observable from 'symbol-observable' import ActionTypes from './utils/actionTypes' import isPlainObject from './utils/isPlainObject' export default function createStore(reducer, preloadedState, enhancer) { // 1. 對傳入參數的順序處理 // 先忽略這一塊 // 2. 變量的定義 let currentReducer = reducer let currentState = preloadedState let currentListeners = [] let nextListeners = currentListeners let isDispatching = false // 3. 一系列函數定義 function ensuerCanMutateNextListeners(){} function getState(){} function subscribe(listener){} function dispatch(action){} function replaceReducer(nextReducer){} function observable(){} // 4. dispatch一個初始化的action dispatch({ type: ActionTypes.INIT }) // 5. 返回store對象 return { dispatch, subscribe, getState, replaceReducer, [$$observable]: observable } }
咱們分別對這五塊來看看。閉包
這一步就是對傳入給 createStore
的三個參數 reducer
、 preloadedState
、 enhancer
的順序調整。app
export default function createStore(reducer, preloadedState, enhancer) { if (typeof preloadedState === 'function' && typeof enhancer === 'undefined') { // 第二個參數是一個函數,沒有第三個參數的狀況 enhancer = preloadedState preloadedState = undefined } if (typeof enhancer !== 'undefined') { if (typeof enhancer !== 'function') { // enhancer 不是函數就報錯 throw new Error('Expected the enhancer to be a function.') } // enhancer就是高階函數,強化了自己這個createStore的函數,拿到加強後的createStore函數去處理 // applyMiddleware這個函數還會涉及到這個 return enhancer(createStore)(reducer, preloadedState) } if (typeof reducer !== 'function') { // reducer不是函數報錯 throw new Error('Expected the reducer to be a function.') } // 其餘代碼省略 }
let currentReducer = reducer let currentState = preloadedState let currentListeners = [] let nextListeners = currentListeners let isDispatching = false
currentReducer
當前 store
的 reducer
,由 createStore
傳入的第一個參數 reducer
初始化currentState
保存當前整個 state
的狀態,初始值就是 createStore
傳進來的第二個參數 preloadedState
,至關於 store
的初始值currentListeners
當前的監聽器,默認是空nextListeners
下一個監聽器,由 currentListeners
賦值isDispatching
當前的 store
是否正在 dispatch
一個action全是閉包保存的變量dom
在 createStore
的最後,dispatch
了一個 { type: ActionTypes.INIT }
對象,那就按圖索驥,從 dispatch
函數開始看。async
先把 ./utils
下的三個輔助函數(actionTypes
、 isPlainObject
、warning
)看一下:函數
actionTypes:this
const randomString = () => Math.random() .toString(36) .substring(7) .split('') .join('.') const ActionTypes = { INIT: `@@redux/INIT${randomString()}`, REPLACE: `@@redux/REPLACE${randomString()}`, PROBE_UNKNOWN_ACTION: () => `@@redux/PROBE_UNKNOWN_ACTION${randomString()}` } export default ActionTypes
這裏返回的都是隨機的 action.type
,爲了區別常規業務開發寫的 action.type
,好比:ActionTypes.INIT
拿到的是一個相似與 @@redux/INITg.f.m.0.0.4
隨機字符串,只有這樣奇奇怪怪的隨機數纔不會和業務中定義的 reducer
所判斷的 type
重複。
isPlainObject:
判斷函數是不是純對象,[1,23]
、new Date()
這些都會返回 false
。
export default function isPlainObject(obj) { if (typeof obj !== 'object' || obj === null) return false let proto = obj while (Object.getPrototypeOf(proto) !== null) { proto = Object.getPrototypeOf(proto) } return Object.getPrototypeOf(obj) === proto }
warning:
就是一個報錯函數
export default function warning(message) { /* eslint-disable no-console */ if (typeof console !== 'undefined' && typeof console.error === 'function') { console.error(message) } /* eslint-enable no-console */ try { // This error was thrown as a convenience so that if you enable // "break on all exceptions" in your console, // it would pause the execution at this line. throw new Error(message) } catch (e) {} // eslint-disable-line no-empty }
dispatch
用過 redux
的都知道,這就是派發 action
的函數,把派發出去的 action
交由 reducer
處理。
function dispatch(action) { if (!isPlainObject(action)) { // action不是純對象報錯 throw new Error( 'Actions must be plain objects. ' + 'Use custom middleware for async actions.' ) } if (typeof action.type === 'undefined') { // action沒有type屬性也報錯 throw new Error( 'Actions may not have an undefined "type" property. ' + 'Have you misspelled a constant?' ) } if (isDispatching) { // 這個store正在dispach別的action的時候不能再dispatch另一個action throw new Error('Reducers may not dispatch actions.') } try { // 當前state和action交由當前的reducer處理 // 同時改變isDispatching 爲 true 代表正在處理action中,不能dispatch新的action了 isDispatching = true currentState = currentReducer(currentState, action) } finally { // 修改成 false ,能夠dispatch新的action isDispatching = false } // 賦值,最終 listeners 、 currentListeners 、nextListeners的值都是 nextListeners const listeners = (currentListeners = nextListeners) for (let i = 0; i < listeners.length; i++) { // 遍歷調用監聽的函數 const listener = listeners[i] listener() } // 返回這個action, 沒什麼做用 return action }
核心代碼就是 currentState = currentReducer(currentState, action)
,傳入 currentState
、action
給 currentReducer
,currentReducer
把返回值賦值給了 currentState
。
訂閱監聽器。
function subscribe(listener) { if (typeof listener !== 'function') { // 不給函數就報錯 throw new Error('Expected the listener to be a function.') } if (isDispatching) { // 正在dispatch一個store的時候是不能訂閱監聽器的 throw new Error( 'You may not call store.subscribe() while the reducer is executing. ' + 'If you would like to be notified after the store has been updated, subscribe from a ' + 'component and invoke store.getState() in the callback to access the latest state. ' + 'See https://redux.js.org/api-reference/store#subscribe(listener) for more details.' ) } // 給unsubscribe調用解除訂閱標識 let isSubscribed = true // 下面解釋爲何要調用這個ensureCanMutateNextListeners函數 ensureCanMutateNextListeners() // 就是簡單的把傳入的listeners放到nextListeners nextListeners.push(listener) // 返回一個解除訂閱的函數 return function unsubscribe() { if (!isSubscribed) { return } if (isDispatching) { throw new Error( 'You may not unsubscribe from a store listener while the reducer is executing. ' + 'See https://redux.js.org/api-reference/store#subscribe(listener) for more details.' ) } isSubscribed = false ensureCanMutateNextListeners() const index = nextListeners.indexOf(listener) // 從 nextListeners 數組中移除 nextListeners.splice(index, 1) } }
訂閱沒什麼問題,就是爲啥用調用 ensureCanMutateNextListeners
呢?
看一下這個函數:
function ensureCanMutateNextListeners() { if (nextListeners === currentListeners) { nextListeners = currentListeners.slice() } }
這個函數就是檢查 nextListeners
和 currentListeners
是不是相同的,若是是相同的就把 currentListeners
拷貝一個新的賦值給nextListeners
。由於數組是引用類型的關係,若是 nextListeners
和 currentListeners
相同,像 nextListeners
中 push
新的 listener
的時候會直接影響到 currentListeners
的值。
注意到另一點,在 dispatch
函數的最後遍歷 listeners
的時候,是這樣操做的: const listeners = (currentListeners = nextListeners)
,這裏 nextListeners
和 currentListeners
就相同了。
那麼爲啥內部須要有 currentListeners
和 nextListeners
,主要是通知訂閱者的過程當中發生了其餘的訂閱(subscribe
)和退訂(unsubscribe
),那確定會發生錯誤或者不肯定性。
這裏有一篇文章論述到這個問題。
簡單的把 store
的 currentState
返回出來。
function getState() { if (isDispatching) { throw new Error( 'You may not call store.getState() while the reducer is executing. ' + 'The reducer has already received the state as an argument. ' + 'Pass it down from the top reducer instead of reading it from the store.' ) } return currentState }
這個 API
幫你替換把原來的 reducer
替換成新的 reducer
。
function replaceReducer(nextReducer) { if (typeof nextReducer !== 'function') { throw new Error('Expected the nextReducer to be a function.') } // nextReducer替換舊的reducer currentReducer = nextReducer // 注意這裏也dispatch了一個隨機action,和createStore的最後dispatch一個隨機的初始化action功能是相同的,都是了初始化state dispatch({ type: ActionTypes.REPLACE }) }
不懂,仍是貼一下代碼:
function observable() { const outerSubscribe = subscribe return { /** * The minimal observable subscription method. * @param {Object} observer Any object that can be used as an observer. * The observer object should have a `next` method. * @returns {subscription} An object with an `unsubscribe` method that can * be used to unsubscribe the observable from the store, and prevent further * emission of values from the observable. */ subscribe(observer) { if (typeof observer !== 'object' || observer === null) { throw new TypeError('Expected the observer to be an object.') } function observeState() { if (observer.next) { observer.next(getState()) } } observeState() const unsubscribe = outerSubscribe(observeState) return { unsubscribe } }, [$$observable]() { return this } } }
dispatch({ type: ActionTypes.INIT })
在最後,dispatch
了一個 type
爲隨機值的 action
, 咱們業務的 reducer
中最後沒有匹配到對用的 action.type
都會默認返回默認的 state
, 而這個默認的 state
每每又在 reducer
函數最開始寫的時候已經給好了默認值,這樣 dispatch
的 action
與任何 reducer
都不匹配,因此拿到了全部 reducer
的默認值從而 currentState
就被更新成了 reducer
定義過的默認值。
把定義好的方法掛載到一個對象上面,這個對象就是 store
對象。
return { dispatch, subscribe, getState, replaceReducer, [$$observable]: observable }
redux
的代碼是真的簡潔,代碼的註釋甚至比代碼自己還要長,仍是很是值得閱讀的。