/** * These are private action types reserved by Redux. * For any unknown actions, you must return the current state. * If the current state is undefined, you must return the initial state. * Do not reference these action types directly in your code. * 定义一个初始化 action, 这个 action 是 redux 私有的,不应该被引用 * 同时 reducer 对于未知的 action 应该返回当前的 state 不作任何处理 */ exportvar ActionTypes = { INIT: '@@redux/INIT' }
/** * 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`. * * @param {Function}reducer A function that returns the next state tree, given * the current state tree and the action to handle. * * @param {any}[preloadedState] The initial state. You may optionally specify it * to hydrate the state from the server in universal apps, or to restore a * previously serialized user session. * If you use `combineReducers` to produce the root reducer function, this must be * an object with the same shape as `combineReducers` keys. * * @param {Function}enhancer The store enhancer. You may optionally specify it * to enhance the store with third-party capabilities such as middleware, * time travel, persistence, etc. The only store enhancer that ships with Redux * is `applyMiddleware()`. * * @returns {Store}A Redux store that lets you read the state, dispatch actions * and subscribe to changes. */ exportdefaultfunctioncreateStore(reducer, preloadedState, enhancer) { // 可省略的参数处理,如果 preloadedState 省略 则把第二个参数作为 enhancer if (typeof preloadedState === 'function' && typeof enhancer === 'undefined') { enhancer = preloadedState preloadedState = undefined }
/** * Reads the state tree managed by the store. * * @returns {any}The current state tree of your application. * 返回应用当前的 state */ functiongetState() { return currentState }
/** * Adds a change listener. It will be called any time an action is dispatched, * and some part of the state tree may potentially have changed. You may then * call `getState()` to read the current state tree inside the callback. * * You may call `dispatch()` from a change listener, with the following * caveats: * * 1. The subscriptions are snapshotted just before every `dispatch()` call. * If you subscribe or unsubscribe while the listeners are being invoked, this * will not have any effect on the `dispatch()` that is currently in progress. * However, the next `dispatch()` call, whether nested or not, will use a more * recent snapshot of the subscription list. * * 2. The listener should not expect to see all state changes, as the state * might have been updated multiple times during a nested `dispatch()` before * the listener is called. It is, however, guaranteed that all subscribers * registered before the `dispatch()` started will be called with the latest * state by the time it exits. * * @param {Function}listener A callback to be invoked on every dispatch. * @returns {Function}A function to remove this change listener. * * 注意: * 1. 函数返回一个取消订阅当前监听函数的函数 * 2. 每次事件列表的改变都在下一次 dispatch 时生效,如果当前正在执行 dispatch 函数, * 则新增加的事件订阅不会立即生效,取消事件订阅也是一样 * 3. 事件监听不应该监听全部的 state 的变化,因为在监听的函数执行之时 state 可能会被改变多次 */ functionsubscribe(listener) { if (typeof listener !== 'function') { thrownewError('Expected listener to be a function.') }
//返回一个取消订阅当前监听函数的函数 returnfunctionunsubscribe() { if (!isSubscribed) { return }
isSubscribed = false
ensureCanMutateNextListeners() //从事件列表中找到刚才添加的,并删除 var index = nextListeners.indexOf(listener) nextListeners.splice(index, 1) } }
/** * Dispatches an action. It is the only way to trigger a state change. * * The `reducer` function, used to create the store, will be called with the * current state tree and the given `action`. Its return value will * be considered the **next** state of the tree, and the change listeners * will be notified. * * The base implementation only supports plain object actions. If you want to * dispatch a Promise, an Observable, a thunk, or something else, you need to * wrap your store creating function into the corresponding middleware. For * example, see the documentation for the `redux-thunk` package. Even the * middleware will eventually dispatch plain object actions using this method. * * @param {Object}action A plain object representing “what changed”. It is * a good idea to keep actions serializable so you can record and replay user * sessions, or use the time travelling `redux-devtools`. An action must have * a `type` property which may not be `undefined`. It is a good idea to use * string constants for action types. * @returns {Object}For convenience, the same action object you dispatched. * * Note that, if you use a custom middleware, it may wrap `dispatch()` to * return something else (for example, a Promise you can await). * 注意: * 1. dispatch 默认只支持 action 作为参数,但是使用了中间件以后,比如 `redux-thunk` * 中间件,指定格式的 function 也被支持作为参数。 * 2. dispatch 的默认返回值是参数中的 action, 但是如果使用了中间件,dispatch 之前和 * 之后可能会执行中间件的一些操作,并且返回值也可能被修改。 * * 以下情况会抛出异常: * 1. `action` 不是一个简单对象 * 2. `action.type` 不存在 * 3. `action` 正在 `dispatching` 然后再次 `dispatch`, 这种情况主要发生在异步 `reducer` */ functiondispatch(action) { if (!isPlainObject(action)) { thrownewError( 'Actions must be plain objects. ' + 'Use custom middleware for async actions.' ) }
if (typeof action.type === 'undefined') { thrownewError( 'Actions may not have an undefined "type" property. ' + 'Have you misspelled a constant?' ) }
if (isDispatching) { thrownewError('Reducers may not dispatch actions.') }
try { isDispatching = true //更新 store 的 state currentState = currentReducer(currentState, action) } finally { isDispatching = false }
//保证当前执行的是最新的事件序列 var listeners = currentListeners = nextListeners //逐个调用订阅的事件,比如 state 改变了更新ui for (var i = 0; i < listeners.length; i++) { listeners[i]() }
return action }
/** * Replaces the reducer currently used by the store to calculate the state. * * You might need this if your app implements code splitting and you want to * load some of the reducers dynamically. You might also need this if you * implement a hot reloading mechanism for Redux. * * 使用新的 reducer 替代 store 当前的 reducer,主要用于一些动态加载的内容, * 比如 webpack 的代码分割功能或者 redux 的代码热更新。 * @param {Function}nextReducer The reducer for the store to use instead. * @returns {void} */ functionreplaceReducer(nextReducer) { if (typeof nextReducer !== 'function') { thrownewError('Expected the nextReducer to be a function.') }
//待补充:这部分内容研究后稍后补充 /** * Interoperability point for observable/reactive libraries. * @returns {observable}A minimal observable of state changes. * For more information, see the observable proposal: * https://github.com/zenparsing/es-observable * ECMAScript标准库引入了一个Observable类型的提案。 * Observable类型可用于建模基于推送的数据源,例如DOM事件,定时器间隔和 socket */ functionobservable() { var 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') { thrownewTypeError('Expected the observer to be an object.') }
functionobserveState() { if (observer.next) { observer.next(getState()) } }
observeState() var unsubscribe = outerSubscribe(observeState) return { unsubscribe } },
[$$observable]() { returnthis } } }
// When a store is created, an "INIT" action is dispatched so that every // reducer returns their initial state. This effectively populates // the initial state tree. // 在createStore的时候,函数内部会dispatch({ type: ActionTypes.INIT }) // 对 state 进行初始化,这也就是说为什么我们的 reducer 需要在 state 为空的时候初始化, //并且能够处理不存在的 action(不要引入处理这个 ActionTypes.INIT 这个 action,而是 //用默认的方式,比如 switch 语句中的 default 分支, 处理返回当前的非空的 store) dispatch({ type: ActionTypes.INIT })