createStore
这个 createStore 函数需要返回一个对象,包含以下属性:
dispatch:分发 action 去改变数据getState:获取当前仓库中的状态subscribe:注册一个监听器,分发 action 后触发;并返回一个取消监听的函数replaceReducer:替换仓库中当前的 reducerSymbol("observable"):内置函数,实现观察者模式 (暂不实现)
代码实现:
/**
* 创建仓库
* @param {function} reducer
* @param {any} defaultState 默认状态值
*/
export default function createStore(reducer, defaultState) {
// 简单验证 reducer 是不是一个函数
if (typeof reducer !== 'function') {
throw new TypeError(`The first parameter reducer must be a function`)
}
let currentReducer = reducer // 当前使用的 reducer
let currentState = defaultState // 当前状态值
const subscribers = [] // 监听器数组
function dispatch(action) {
// 验证 action 类型
if (!_isPlainObject(action)) {
throw new TypeError(`The required parameter action must be a "Plain Object"`)
}
if (action.type === undefined) {
throw new TypeError(`The required parameter action must have a property of "type"`)
}
currentState = currentReducer(currentState, action)
// 执行所有的监听器
subscribers.forEach(subscriber => {
subscriber()
})
}
function getState() {
return currentState
}
// 添加监听器
function subscribe(subscriber) {
subscribers.push(subscriber)
let isRemoved = false // 判定是否已移除
return () => {
if (isRemoved) return
const index = subscribers.indexOf(subscriber)
subscribers.splice(index, 1)
isRemoved = true
}
}
function replaceReducer(newReducer) {
// 简单验证 reducer 是不是一个函数
if (typeof newReducer !== 'function') {
throw new TypeError(`The first parameter reducer must be a function`)
}
currentReducer = newReducer
}
// 创建仓库时,需要调用一次 dispatch 分发 action,完成状态初始化
dispatch({
type: `@@redux/INIT${_getRandomStr(7)}`
})
return {
dispatch,
getState,
subscribe,
replaceReducer
}
}
涉及的辅助函数:
/**
* 判断此对象是否是平面对象
* @param {Object} o 目标对象
* @returns boolean
*/
function _isPlainObject(o) {
if (typeof o !== 'object') {
return false
}
return Object.getPrototypeOf(o) === Object.prototype
}
/**
* 得到一个指定长度的字符串,例如:'f.a.4.d.7'
* @param {number} len 字串长度
*/
function _getRandomStr(len = 7) {
// 转 36 进制,即:26 字母 + 10 数字
return Math.random().toString(36).substr(2, len).split('').join('.')
}
bindActionCreators
通过传递两个参数:(1) action 创建函数 或 action 创建函数的对象;(2) dispatch 函数
从而获得增强功能的 action 创建函数 或 action 创建函数的对象
/**
* 增强 action 创建函数
* @param {*} actionCreators
* @param {*} dispatch
*/
export default function bindActionCreators(actionCreators, dispatch) {
if (typeof actionCreators === 'function') {
return _getAutoDispatchActionCreator(actionCreators, dispatch)
} else if (
actionCreators !== null &&
typeof actionCreators === 'object' &&
!Array.isArray(actionCreators)
) {
const res = {}
for (const key in actionCreators) {
if (Object.hasOwnProperty.call(actionCreators, key)) {
const actionCreator = actionCreators[key]
if (typeof actionCreator === 'function') {
res[key] = _getAutoDispatchActionCreator(actionCreator, dispatch)
}
}
}
return res
} else {
throw new TypeError(`The first parameter of actionCreators must be an "object" or "function" which means action creator`)
}
}
/**
* 得到自动分发 action 的创建函数
* @param {*} actionCreators
* @param {*} dispatch
*/
function _getAutoDispatchActionCreator(actionCreators, dispatch) {
return (...args) => {
const action = actionCreators(...args)
dispatch(action)
}
}
combineReducers
功能:组装 reducers,返回一个 reducer,数据使用一个对象表示,对象的属性名与传递的参数对象保持一致
调用它合并 reducers 时,每个 reducer 会分发两个特殊类型的 action,用于确保返回的 state 不是 undefined
实现如下:
/**
* 合并 reducers
* @param {Object}} reducers
* @return {Funtion} reducer
*/
export default function combineReducers(reducers) {
_validateReducers(reducers)
return function (state = {}, action) {
const newState = {}
for (const key in reducers) {
if (Object.hasOwnProperty.call(reducers, key)) {
const reducer = reducers[key]
newState[key] = reducer(state[key], action)
}
}
return newState
}
}
/**
* 验证 reducers
* @param {*} reducers
*/
function _validateReducers(reducers) {
if (
typeof reducers !== 'object' ||
reducers === null ||
Array.isArray(reducers)
) {
throw new TypeError(`reducers must be an "object"`)
}
if (!isPlainObject(reducers)) {
throw new TypeError(`reducers must be a "plain object"`)
}
// 验证每个 reducer 的返回结果是不是 undefined
for (const key in reducers) {
if (Object.hasOwnProperty.call(reducers, key)) {
const reducer = reducers[key]
let state = reducer(undefined, {
type: `@@redux/INIT${_getRandomStr(7)}`
})
if (state === undefined) {
throw new TypeError(`reducer returns state cannot be undefined`)
}
state = reducer(undefined, {
type: `@@redux/PROBE_UNKNOWN_ACTION${_getRandomStr(7)}`
})
if (state === undefined) {
throw new TypeError(`reducer returns state cannot be undefined`)
}
}
}
}