createStore中用到的主要知识点有:
1.原型及原型链
2.闭包
3.高阶函数
4.其中action派发动作的监听类似于订阅发布模式
概述
* Creates a Redux store that holds the state tree.
* The only way to change the data in the store is to call `dispatch()` on it.
*
* There should only be a single store in your app. To specify how different
* parts of the state tree respond to actions, you may combine several reducers
* into a single reducer function by using `combineReducers`.
createStore函数是用来创建一个保存有整个状态树的store。要想改变store中的数据,唯一方式是执行其中的dispatch方法 。并且在你的应用程序中只能有一个单独的 store。为了指定状态树的不同部分如何响应操作,你可以通过combineReducers函数将几个reducer函数合并到一个单独的reducer函数。
本文将主要介绍下面两个方法:
dispatch-派发一个action,它是改变状态树的唯一途径。
subscribe-监听状态树的变化,任何时候派发一个action,都将调用当前的所有监听回调函数。
dispatch函数
if (!isPlainObject(action)) {
throw new Error(
'Actions must be plain objects. ' +
'Use custom middleware for async actions.'
)
}
/**
* @param {any} obj The object to inspect.
* @returns {boolean} True if the argument appears to be a plain object.
*/
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
}
isPlainObject判断action是不是一个简单对象,即原型链只有一层,Object.getPrototype(action) === Object.prototype,如对象字面量创建的对象。而构造函数或class创建的对象就不符合要求。
if (typeof action.type === 'undefined') {
throw new Error(
'Actions may not have an undefined "type" property. ' +
'Have you misspelled a constant?'
)
}
action的type属性不能为undefined。
if (isDispatching) {
throw new Error('Reducers may not dispatch actions.')
}
不能同时派发多个action。
try {
isDispatching = true
currentState = currentReducer(currentState, action)
} finally {
isDispatching = false
}
执行当前reducer函数获取新的state。
const listeners = (currentListeners = nextListeners)
for (let i = 0; i < listeners.length; i++) {
const listener = listeners[i]
listener()
}
执行所有监听回调函数。
subscribe函数
if (typeof listener !== 'function') {
throw new Error('Expected the listener to be a function.')
}
if (isDispatching) {
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.'
)
}
传给subscribe参数的必须是function,且在派发action时不允许调用该方法。
let isSubscribed = true
ensureCanMutateNextListeners()
nextListeners.push(listener)
function ensureCanMutateNextListeners() {
if (nextListeners === currentListeners) {
nextListeners = currentListeners.slice()
}
}
其中isSubscribed因为闭包的原因,在subscribe执行后返回的函数中可以获取该变量,并确保后面的删除操作只执行一次。ensureCanMutateNextListeners函数确保nextListeners和currentListeners指向不同的对象。
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.splice(index, 1)
}
subscribe返回的是一个函数,用来删除当前传入的监听函数。将isSubscribed赋值为false用来确保只执行一次。
介绍完dispatch和subscribe,现在来看一下createStore整体执行流程。
if (
(typeof preloadedState === 'function' && typeof enhancer === 'function') ||
(typeof enhancer === 'function' && typeof arguments[3] === 'function')
) {
throw new Error(
'It looks like you are passing several store enhancers to ' +
'createStore(). This is not supported. Instead, compose them ' +
'together to a single function'
)
}
if (typeof preloadedState === 'function' && typeof enhancer === 'undefined') {
enhancer = preloadedState
preloadedState = undefined
}
当第二、三个实参都是函数或第三个且第四个实参都是函数时,抛出异常,提醒将中间件合并到一个函数;当第二个参数为函数且第三个参数没传时,第二个参数作为中间件;
if (typeof enhancer !== 'undefined') {
if (typeof enhancer !== 'function') {
throw new Error('Expected the enhancer to be a function.')
}
return enhancer(createStore)(reducer, preloadedState)
}
if (typeof reducer !== 'function') {
throw new Error('Expected the reducer to be a function.')
}
reducer必须为函数,否则抛异常。当enhancer有值时,会用到函数柯理化求值。
网友评论