回顾一下 redux
的目录结构:
.REDUXSRC
│ applyMiddleware.js
│ bindActionCreators.js
│ combineReducers.js
│ compose.js
│ createStore.js
│ index.js
│
└─utils
actionTypes.js
isPlainObject.js
warning.js
redux
在 index.js
中一共暴露了5个 API
, 上一篇文章讲了下和 redux
关联性不太大的 compose
。现在正式讲一讲最核心的 createStore
。
createStore.js
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
}
}
我们分别对这五块来看看。
1. 参数的顺序处理
这一步就是对传入给 createStore
的三个参数 reducer
、 preloadedState
、 enhancer
的顺序调整。
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.')
}
// 其他代码省略
}
2. 变量的定义
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
全是闭包保存的变量
3. 函数的定义
在 createStore
的最后,dispatch
了一个 { type: ActionTypes.INIT }
对象,那就按图索骥,从 dispatch
函数开始看。
先把 ./utils
下的三个辅助函数(actionTypes
、 isPlainObject
、warning
)看一下:
actionTypes:
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
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
。
subscribe
订阅监听器。
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
),那肯定会发生错误或者不确定性。
这里有一篇文章论述到这个问题。
getState
简单的把 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
}
replaceReducer
这个 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 })
}
observable
不懂,还是贴一下代码:
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
}
}
}
4. dispatch一个初始化的action
dispatch({ type: ActionTypes.INIT })
在最后,dispatch
了一个 type
为随机值的 action
, 我们业务的 reducer
中最后没有匹配到对用的 action.type
都会默认返回默认的 state
, 而这个默认的 state
往往又在 reducer
函数最开始写的时候已经给好了默认值,这样 dispatch
的 action
与任何 reducer
都不匹配,所以拿到了所有 reducer
的默认值从而 currentState
就被更新成了 reducer
定义过的默认值。
5. 返回的store对象
把定义好的方法挂载到一个对象上面,这个对象就是 store
对象。
return {
dispatch,
subscribe,
getState,
replaceReducer,
[$$observable]: observable
}
总结
redux
的代码是真的简洁,代码的注释甚至比代码本身还要长,还是非常值得阅读的。
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。