嗯,就是学习一下,读读源码。顺便把源码里的文档翻译了一下并添加了一些简单的注释。
本文对应的文件是src/createStore.js
import isPlainObject from 'lodash/isPlainObject'
import $$observable from 'symbol-observable'
/**
* ActionTypes里定义的是Redux保留的私有action。
* 对于任何未知的action,你必须返回store的当前状态。
* 如果传入的当前状态是undefined,你必须返回store的初始状态。
* 不要在应用代码中直接引用这些action。
*/
export var ActionTypes = {
INIT: '@@redux/INIT'
}
/**
* createStore方法用于创建一个保存程序状态的store。
* 改变store中数据的唯一方法是调用store的`dispatch()`方法。
*
* 你的应用中应该只有一个store。为了将程序状态中不同部分的变更逻辑
* 组合在一起,你可以通过`combineReducers`方法将多个reducer组合成一个reducer。
*
* @param {Function} reducer 一个返回应用下一状态的函数,入参是程序的当前状态以及
* 要发送的action。
*
* @param {any} [preloadedState] store的初始状态。你可以选择性的为store指定一个
* 初始状态。
* 如果你使用了`combineReducers`方法来生成最终的reducer。那么这个初始状态对象的
* 结构必须与调用`combineReducers`方法时传入的参数的结构保持对应关系。
*
* @param {Function} enhancer store增强器。你可以选择性的传入一个增强函数来扩展
* store的功能,例如中间件,时间旅行,持久化等。Redux自带的唯一一个增强器是
* `applyMiddleware()`方法。
*
* @returns {Store} 返回一个可以读取状态,发送action以及订阅变更通知的Redux store。
*/
export default function createStore(reducer, preloadedState, enhancer) {
// 如果只传入reducer和enhancer,则store的初始状态为undefined
if (typeof preloadedState === 'function' && typeof enhancer === 'undefined') {
enhancer = preloadedState
preloadedState = undefined
}
// enhancer必须是一个函数
if (typeof enhancer !== 'undefined') {
if (typeof enhancer !== 'function') {
throw new Error('Expected the enhancer to be a function.')
}
// 返回使用enhancer增强后的store
return enhancer(createStore)(reducer, preloadedState)
}
// reducer必须是一个函数
if (typeof reducer !== 'function') {
throw new Error('Expected the reducer to be a function.')
}
var currentReducer = reducer
var currentState = preloadedState
var currentListeners = []
var nextListeners = currentListeners
var isDispatching = false
// 在每次修改监听函数数组之前复制一份,实际的修改发生在这个新
// 复制出来的数组上。确保在某次dispatch发生前就存在的监听器,
// 在该次dispatch之后都能被触发一次。
function ensureCanMutateNextListeners() {
if (nextListeners === currentListeners) {
nextListeners = currentListeners.slice()
}
}
/**
* 读取store状态
*
* @returns {any} 返回store的当前状态
*/
function getState() {
return currentState
}
/**
*
* 新增一个变更监听函数。每当dispatch了一个action之后监听函数都会被触发一次。
* 你可以在监听函数中通过`getState()`方法获取store的最新状态。
*
* 你可以在一个回调函数中再次调用`dispatch()`。但需要注意以下两点:
*
* 1. 在每一次dispatch()调用执行之前,监听函数数组都会被复制一份(通过前文提到
* 的ensureCanMutateNextListeners方法)。如果你在监听函数中增加或删除其他监听函
* 数,那么这些操作并不会影响到当前进行中的这一轮dispatch。而下一次dispatch,
* 不论其是否是嵌套调用,都会使用最新的,修改后的监听函数列表。
*
* 2. 由于在一个监听函数执行前可能已经进行了多次嵌套的dispatch调用,因此不能保证
* 每个监听函数都可以获取到所有的状态变更。然而,可以确定的是,在某次dispatch
* 触发之前已经注册的监听函数都可以读取到这次diapatch之后store的最新状态。
*
* @param {Function} listener 每次dispatch之后执行的回调函数
* @returns {Function} 返回一个用于取消这次订阅的函数
*/
function subscribe(listener) {
if (typeof listener !== 'function') {
throw new Error('Expected listener to be a function.')
}
var isSubscribed = true
// 添加监听函数前确保只操作当前数组的一份拷贝
ensureCanMutateNextListeners()
nextListeners.push(listener)
return function unsubscribe() {
if (!isSubscribed) {
return
}
isSubscribed = false
// 移除监听函数前确保只操作当前数组的一份拷贝
ensureCanMutateNextListeners()
var index = nextListeners.indexOf(listener)
nextListeners.splice(index, 1)
}
}
/**
* 发送一个action。这是触发状态变更的唯一方法。
*
* 每次发送action的时候,用于创建store的`reducer`函数都会被调用一次。调用时
* 传入的参数是当前的状态以及被发送的`action`。调用的返回值会被当做更新后的
* 状态。调用完成后,所有的状态监听函数都会被触发。
*
* 基础实现中仅支持发送形式为简单对象的action。如果你希望可以发送Promise,Observable,
* thunk或是其它形式的action,你需要使用相应的中间件把store创建函数封装起来。
* 你可以在`redux-thunk`模块的文档中找到这方面的示例。不过即使在这些中间件内部
* 还是通过dispatch方法发送了简单对象形式的action。
*
* @param {Object} action 一个表示变更内容的对象。保证你的action是可被序列化的
* 是一种很好的实践,这样你就可以记录并回放用户的操作,或是使用可以穿梭时间
* 的`redux-devtools`插件。一个action必须有一个值不为`undefined`的`type`属性。
* 推荐你使用字符串常量来表示action类型。
*
* @returns {Object} 为了方便起见,返回你传入的action对象。
*
* 要注意的是,如果你使用了一个自定义的中间件,它可能会把`dispatch()`的返回值
* 封装成其它内容(比如,一个你可以await的Promise)
*/
function dispatch(action) {
// 如果action不是简单对象,抛出异常
if (!isPlainObject(action)) {
throw new Error(
'Actions must be plain objects. ' +
'Use custom middleware for async actions.'
)
}
// 如果action的类型是undefined,抛出异常
if (typeof action.type === 'undefined') {
throw new Error(
'Actions may not have an undefined "type" property. ' +
'Have you misspelled a constant?'
)
}
// reducer内部不允许再次调用dispatch,否则抛出异常
if (isDispatching) {
throw new Error('Reducers may not dispatch actions.')
}
// 执行reducer,传入当前状态和action
try {
isDispatching = true
currentState = currentReducer(currentState, action)
} finally {
isDispatching = false
}
// 触发所有的状态监听回调函数
var listeners = currentListeners = nextListeners
for (var i = 0; i < listeners.length; i++) {
listeners[i]()
}
return action
}
/**
* 替换store当前使用的reducer函数
*
* 如果你的应用程序实现了代码拆分并且你希望动态加载某些reducer的时候你
* 可能会用到这个方法。或者当你要为Redux实现一个热加载机制的时候,你也
* 会用到它。
*
* @param {Function} nextReducer 要使用的新reducer
*
* @returns {void}
*/
function replaceReducer(nextReducer) {
if (typeof nextReducer !== 'function') {
throw new Error('Expected the nextReducer to be a function.')
}
currentReducer = nextReducer
dispatch({ type: ActionTypes.INIT })
}
/**
* 为observable/reactive库预留的交互接口。
* @returns {observable} 表示状态变更的最简单的observable对象
* 想要获取更多信息,可以查看observable提案:
* https://github.com/zenparsing/es-observable
*/
function observable() {
// 首先保留对Redux中subscribe方法的引用,在observable的世界里
// 有一个同名方法。
var outerSubscribe = subscribe
return {
/**
* 一个极简的observable订阅方法。
* @param {Object} observer 任何可以作为observer使用的对象
* observer对象应该包含一个`next`方法。
* @returns {subscription} 返回一个带有`unsbscribe`方法的对象。该
* 方法将用于停止接收来自store的状态变更信息。
*/
subscribe(observer) {
// observer参数必须是一个对象,否则抛出异常。但并未检测是否有next方法。。。
if (typeof observer !== 'object') {
throw new TypeError('Expected the observer to be an object.')
}
// 创建一个状态变更回调函数。逻辑很简单,把store最新的状态传给observer
function observeState() {
if (observer.next) {
observer.next(getState())
}
}
// 立即执行一次回调函数,把当前状态传给observer
observeState()
var unsubscribe = outerSubscribe(observeState)
return { unsubscribe }
},
// 根据observable提案,[Symbol.observable]()返回observable对象自身
[$$observable]() {
return this
}
}
}
// store创建好以后,立即发送一个初始化action。这样做是为了让reducer
// 返回store的初始状态(当给reducer传入的当前state为undefined时,reducer
// 会返回store的初始状态)。
dispatch({ type: ActionTypes.INIT })
// 返回创建好的store对象
return {
dispatch,
subscribe,
getState,
replaceReducer,
[$$observable]: observable
}
}