/** 创建状态树
* @param {Function} reducer:reducer就是你写的函数,actionType---->new state
* @param {any} preloadedState: store的初始状态,可以是任何形式,可以是函数,因为有的初始状态可能需要从服务端获取数据进行merge
* @param {Function} enhancer: 中间件,在action发出之前经历的一系列骚操作
* @returns {Store} :返回值是一个对象,可以对Store进行一系列操作,订阅、获取等
*/
export default function createStore(reducer, preloadedState, enhancer) {
// 在源码中,将preloadedState和enhancer可以看作是同一个作用的,就是在生成初始状态之前进行的一系列操作
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
}
/**
* 如果定义了中间件,要到中间件中去执行相应的操作,传入的这几个参数有什么用?待会在中间件中解释
* @params {createStore}
* @params {reducer, preloadedState}
*/
if (typeof enhancer !== 'undefined') {
if (typeof enhancer !== 'function') {
throw new Error('Expected the enhancer to be a function.')
}
return enhancer(createStore)(reducer, preloadedState)
}
/**
* 如果没有中间件,那么我们就继续执行,创建Store
* @variable
* -currentReducer:当前的reducer,也就是你传入的reducer
* -currentState:当前的状态,你传入的state的初始值,如果没有传入,undefiend
* -currentListener:既然是发布订阅,listener是用来监听的
* -nextListener:下一个监听的
* -isDispatching:用来判断是否生成新的状态,默认在生成状态的时候,不允许获取状态,相当于加了一个锁,理由嘛,很简单,防止获取旧状态
*/
let currentReducer = reducer
let currentState = preloadedState
let currentListeners = []
let nextListeners = currentListeners
let isDispatching = false
/**
* 接下来就是把对store的所有操作作为一个对象返回
*/
return {
dispatch,
subscribe,
getState,
replaceReducer,
[$$observable]: observable
}
}
/**
* Dispatches an action. 是唯一改变store的方法
*
* @param {Object} action 必须是一个对象,plainObject
*
* @returns {Object} 返回值和你输入的一样,没有也是一样的
*
* 值得注意的是, 如果你是自定义的中间件, 需要把`dispatch()`包装下,返回其他的东西,最终都是返回一个plainObject
*
* 具体的逻辑,大家自己看吧,本来加了一些注释,但是发现不加注释可读性更好
*/
function dispatch(action) {
try {
isDispatching = true
currentState = currentReducer(currentState, action)
} finally {
isDispatching = false
}
const listeners = (currentListeners = nextListeners)
for (let i = 0; i < listeners.length; i++) {
const listener = listeners[i]
listener()
}
return action
}
/**
* 其实就做了一件事情,把回调函数callback放进数组里面去
* @param {Function} 当dispatch的时候的回调函数
* @returns {Function} 返回这个取消订阅的函数
*/
function subscribe(listener) {
let isSubscribed = true
ensureCanMutateNextListeners()
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.splice(index, 1)
}
}
/**
* @returns {any} 简单得不能再简单的一个操作
*/
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
}
/**
* @param {Function} 动态加载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.REPLACE })
}
/**引用了observabla的库,这是一个提案,为了以后的兼容性,如果没有这个Redux一样可以运行,有兴趣的可以了解
* [Observable提案](https://github.com/tc39/proposal-observable)
* [Observable说明](https://distums.github.io/2017/03/19/observables-proposal-for-ecmascript/)
* subscribe : 是前面的监听回调函数
*/
function observable() {
const outerSubscribe = subscribe
return {
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
}
}
}
/**
* redux中间件的源码很简洁,就是
* @param {...Function} middlewares 一系列中间件
* @returns {Function} 返回值是enchancer去执行中间件
* 参考前面中间件执行的时候,把reducer和state穿进去,然后中间件进行其他操作
* return enhancer(createStore)(reducer, preloadedState)
*/
export default function applyMiddleware(...middlewares) {
return createStore => (...args) => {
const store = createStore(...args)
let dispatch = () => {
throw new Error(
`Dispatching while constructing your middleware is not allowed. ` +
`Other middleware would not be applied to this dispatch.`
)
}
const middlewareAPI = {
getState: store.getState,
dispatch: (...args) => dispatch(...args)
}
const chain = middlewares.map(middleware => middleware(middlewareAPI))
dispatch = compose(...chain)(store.dispatch)
return {
...store,
dispatch
}
}
}
上面的redux基本原理的源码,基本就结束了(是不是太简单了),除了上面之外,还提供了几个工具函数compose、combineReducers、bindActionCreators三个工具函数
compose,主要用在组合中间件的时候,将中间件组合在一起,毕竟只能接受一个函数,而且要求函数按照顺序一个一个执行
/**
* compose就干了一件事情,把函数按照先后顺序调用
* compose (f, g, h)===> (...args) => f(f(h(...args)))
* reduce就是按顺序执行的函数,最常用的就是求和
*/
function compose(...funcs) {
if (funcs.length === 0) {
return arg => arg
}
if (funcs.length === 1) {
return funcs[0]
}
return funcs.reduce((a, b) => (...args) => a(b(...args)))
}
combineReducers,主要用来将多个reducers组合为一个reducers,写过redux的一般会写Containers,每个containers都有reducers,那么怎么将这些reducers组合在一起,combineReducers做的就是这件事。毕竟Redux要求只有一个Store只有一个Reducers。
/**
* @param {Object} 输入参数为一个对象,对象的值为各个reducers
*
* @returns {Function} 返回值就是一个将所有reducers包裹在一起的函数
* 新建了一个finalReducers对象,对象中 键为reducers的键,值为各个reducers(感觉有点废话,但是确实是这样的)
*/
export default function combineReducers(reducers) {
const reducerKeys = Object.keys(reducers)
const finalReducers = {}
for (let i = 0; i < reducerKeys.length; i++) {
const key = reducerKeys[i]
if (typeof reducers[key] === 'function') {
finalReducers[key] = reducers[key]
}
}
const finalReducerKeys = Object.keys(finalReducers)
let unexpectedKeyCache
if (process.env.NODE_ENV !== 'production') {
unexpectedKeyCache = {}
}
return function combination(state = {}, action) {
let hasChanged = false
const nextState = {}
for (let i = 0; i < finalReducerKeys.length; i++) {
const key = finalReducerKeys[i]
const reducer = finalReducers[key]
const previousStateForKey = state[key]
const nextStateForKey = reducer(previousStateForKey, action)
if (typeof nextStateForKey === 'undefined') {
const errorMessage = getUndefinedStateErrorMessage(key, action)
throw new Error(errorMessage)
}
nextState[key] = nextStateForKey
hasChanged = hasChanged || nextStateForKey !== previousStateForKey
}
return hasChanged ? nextState : state
}
}
bindActionCreator的作用是为了隐藏dispatch,将dispatch变成一个可执行函数。
核心就是bindActionCreators
function bindActionCreator(actionCreator, dispatch) {
return function() {
return dispatch(actionCreator.apply(this, arguments))
}
}
但是可能有很多actions,所以就需要便利操作,为每一个action隐藏dispatch
export default function bindActionCreators(actionCreators, dispatch) {
if (typeof actionCreators === 'function') {
return bindActionCreator(actionCreators, dispatch)
}
const keys = Object.keys(actionCreators)
const boundActionCreators = {}
for (let i = 0; i < keys.length; i++) {
const key = keys[i]
const actionCreator = actionCreators[key]
if (typeof actionCreator === 'function') {
boundActionCreators[key] = bindActionCreator(actionCreator, dispatch)
}
}
return boundActionCreators
}
如何判断一个对象是PlainObject,自行理解吧,是在写不动了,再不行,就翻翻课本的原型链。
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
}
Redux git仓库