Redux源码
date
Sep 16, 2021
slug
redux
status
Published
tags
React
源码
summary
从源码解析Redux原理
type
Post
目录结构
|-- redux
|-- applyMiddleware.ts // 组合多个middleware 生成一个enhancer
|-- bindActionCreators.ts // 绑定 dispatch 和 actionCreator
|-- combineReducers.ts // 组合多个reducer 生成一个reducer
|-- compose.ts // 组合多个enhancer 生成一个enhancer
|-- createStore.ts // 接受 reducer [,preloadState][,enhancer] 生成一个store
|-- index.ts // 入口文件 暴露接口
|-- types // 类型定义
| |-- actions.ts
| |-- middleware.ts
| |-- reducers.ts
| |-- store.ts
|-- utils
|-- actionTypes.ts // 生成随机常量
|-- isPlainObject.ts // 判断是否为普通对象
|-- symbol-observable.ts // 提供给其他响应式库的接口
|-- warning.ts // 警告
createStore
export default function createStore<
S,
A extends Action,
Ext = {},
StateExt = never
>(
reducer: Reducer<S, A>,
preloadedState?: PreloadedState<S> | StoreEnhancer<Ext, StateExt>,
enhancer?: StoreEnhancer<Ext, StateExt>
): Store<ExtendState<S, StateExt>, A, StateExt, Ext> & Ext {
// 参数检查
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.'
)
}
// 判断是否传入preloadedState
if (typeof preloadedState === 'function' && typeof enhancer === 'undefined') {
enhancer = preloadedState as StoreEnhancer<Ext, StateExt>
preloadedState = undefined
}
// 判断是否传入enhancner,返回增强后的preloadedState
if (typeof enhancer !== 'undefined') {
// 判断enhancer是否function
if (typeof enhancer !== 'function') {
throw new Error('Expected the enhancer to be a function.')
}
return enhancer(createStore)(
reducer,
preloadedState as PreloadedState<S>
) as Store<ExtendState<S, StateExt>, A, StateExt, Ext> & Ext
}
// 判断reducer是否function
if (typeof reducer !== 'function') {
throw new Error('Expected the reducer to be a function.')
}
let currentReducer = reducer
let currentState = preloadedState as S
let currentListeners: (() => void)[] | null = []
let nextListeners = currentListeners
let isDispatching = false
// 浅拷贝一份当前的listeners
function ensureCanMutateNextListeners() {
if (nextListeners === currentListeners) {
nextListeners = currentListeners.slice()
}
}
// 获取state
function getState(): S {
// 判断是否正在dispatch
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.'
)
}
// 返回当前state
return currentState as S
}
// 订阅器,监听state改变
function subscribe(listener: () => void) {
// 参数判断
if (typeof listener !== 'function') {
throw new Error('Expected the listener to be a function.')
}
// 判断是否在dispatch
if (isDispatching) {
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/store#subscribelistener for more details.'
)
}
// 是否监听boolean初始化
let isSubscribed = true
// 浅拷贝
ensureCanMutateNextListeners()
// 加入集合
nextListeners.push(listener)
// 返回一个取消监听的方法
return function unsubscribe() {
// 判断是否监听boolean 不在监听就return
if (!isSubscribed) {
return
}
// 判断是否正在dispatch
if (isDispatching) {
throw new Error(
'You may not unsubscribe from a store listener while the reducer is executing. ' +
'See https://redux.js.org/api/store#subscribelistener for more details.'
)
}
// 将是否监听boolean改为false
isSubscribed = false
// 浅拷贝,并从集合中删除
ensureCanMutateNextListeners()
const index = nextListeners.indexOf(listener)
nextListeners.splice(index, 1)
currentListeners = null
}
}
// 分发action
function dispatch(action: A) {
// 判断是否是普通对象
if (!isPlainObject(action)) {
throw new Error(
'Actions must be plain objects. ' +
'Use custom middleware for async actions.'
)
}
// 判断action的type属性
if (typeof action.type === 'undefined') {
throw new Error(
'Actions may not have an undefined "type" property. ' +
'Have you misspelled a constant?'
)
}
// 判断是否正在dispatch
if (isDispatching) {
throw new Error('Reducers may not dispatch actions.')
}
// 执行reducer
try {
// 标记正在dispatch
isDispatching = true
currentState = currentReducer(currentState, action)
} finally {
// 结束后标记结束dispatch
isDispatching = false
}
// 依次执行listeners中的监听事件
const listeners = (currentListeners = nextListeners)
for (let i = 0; i < listeners.length; i++) {
const listener = listeners[i]
listener()
}
return action
}
// 替换reducer,用于redux热重载、代码分割或动态加载reducer
function replaceReducer<NewState, NewActions extends A>(
nextReducer: Reducer<NewState, NewActions>
): Store<ExtendState<NewState, StateExt>, NewActions, StateExt, Ext> & Ext {
// reducer类型判断
if (typeof nextReducer !== 'function') {
throw new Error('Expected the nextReducer to be a function.')
}
// TODO: do this more elegantly
// 修改当前reducer
;((currentReducer as unknown) as Reducer<
NewState,
NewActions
>) = nextReducer
// dispatch一个type为随机字符串,与ActionTypes.INIT效果相同,初始化一个新的state
dispatch({ type: ActionTypes.REPLACE } as A)
// change the type of the store by casting it to the new store
return (store as unknown) as Store<
ExtendState<NewState, StateExt>,
NewActions,
StateExt,
Ext
> &
Ext
}
// 提供给观察订阅者/响应式库方法
function observable() {
// subscribe是上面的监听器,检测state变化
const outerSubscribe = subscribe
return {
//传入一个有next()方法的对象监听
// 返回了一个取消监听的方法的对象
subscribe(observer: unknown) {
// 判断类型
if (typeof observer !== 'object' || observer === null) {
throw new TypeError('Expected the observer to be an object.')
}
function observeState() {
const observerAsObserver = observer as Observer<S>
// 判断传入对象是否有next的方法,并将state作为参数传入执行
if (observerAsObserver.next) {
observerAsObserver.next(getState())
}
}
observeState()
const unsubscribe = outerSubscribe(observeState)
return { unsubscribe }
},
[$$observable]() {
return this
}
}
}
// 初始化state
dispatch({ type: ActionTypes.INIT } as A)
const store = ({
dispatch: dispatch as Dispatch<A>,
subscribe,
getState,
replaceReducer,
[$$observable]: observable
} as unknown) as Store<ExtendState<S, StateExt>, A, StateExt, Ext> & Ext
return store
}
- 如果有 enhancer 增强函数则调用增强后的 createStore 初始化
- 利用闭包的原理,保存各类变量的状态
- 定义各类函数暴露给用户使用
- 初始化
getState
获取当前state
subscribe
订阅器,监听state改变
- 对先前的监听函数集合进行浅拷贝备份
- 然后将新的监听函数填充进去
- 返回一个取消监听的函数
- 浅拷贝备份
- 将监听事件从集合中剔除
dispatch
分发action
- 将 action 分发到当前的 reducer 中(真正的改值在 reducer 中)
- 遍历监听函数,并执行
- 返回传入的 action
replaceReducer
替换reducer,用于redux热重载、代码分割或动态加载reducer
- 覆盖当前reducer
- 初始化状态
observable
提供给观察订阅者/响应式库方法,可看
applyMiddleware
export default function applyMiddleware(
...middlewares: Middleware[]
): StoreEnhancer<any> {
return (createStore: StoreEnhancerStoreCreator) => <S, A extends AnyAction>(
reducer: Reducer<S, A>,
preloadedState?: PreloadedState<S>
) => {
// 初始化一个store
const store = createStore(reducer, preloadedState)
// 正在构造时无法dispatch,之后会被compose后的dispatch覆盖
let dispatch: Dispatch = () => {
throw new Error(
'Dispatching while constructing your middleware is not allowed. ' +
'Other middleware would not be applied to this dispatch.'
)
}
const middlewareAPI: MiddlewareAPI = {
getState: store.getState,
dispatch: (action, ...args) => dispatch(action, ...args)
}
// 将每个中间件enhancer增强store
const chain = middlewares.map(middleware => middleware(middlewareAPI))
// 用compose将多个enhancer组合成一个
dispatch = compose<typeof dispatch>(...chain)(store.dispatch)
// 返回之前的store和组合后的dispatch
// 包装后的dispatch是经过多个enhancer处理的dispatch,真正的redux内部dispatch在最后一个middleware内
return {
...store,
dispatch
}
}
}
- 初始化store
- 将每个中间件enhancer增强store
- 用compose将多个enhancer组合成一个
- 返回之前的store和组合后的dispatch
- 开始定义的dispatch会被多个enhancer处理后的dispatch覆盖,真正的redux内部dispatch在最后一个middleware内
假设我们有两个中间件 saga, thunk
他们都有会生成一个类似 dispatch 的函数,假设为 createSagaDispatch, createThunkDispatch
然后 compose(createSagaDispatch, createThunkDispatch)(store.dispatch)
等于 createSagaDispatch(createThunkDispatch(store.dispatch))
效果 createSagaDispatch(store.dispatch) --> sagaDispatch
compose
export default function compose(...funcs: Function[]) {
if (funcs.length === 0) {
// infer the argument type so it is usable in inference down the line
return <T>(arg: T) => arg
}
if (funcs.length === 1) {
return funcs[0]
}
// 用reduce实现将b的执行结果作为参数,传递给a执行;
// 传入enhancer的执行顺序是从后往前
return funcs.reduce((a, b) => (...args: any) => a(b(...args)))
}
将多个函数整合成一个
假设有三个增强函数 funcs: [one, two, three]
然后实际调用的效果为 three(two(one(args)))
one(args) 返回 oneResult
two(oneResult) 返回 twoResult
three(twoResult) 返回 finalResult
combineReducers
export default function combineReducers(reducers: ReducersMapObject) {
// 拿到所有reducer的key
const reducerKeys = Object.keys(reducers)
// 用一个对象来存储最终的reducer
const finalReducers: ReducersMapObject = {}
// 遍历
for (let i = 0; i < reducerKeys.length; i++) {
// 拿key
const key = reducerKeys[i]
// 判断是否生产环境,如果不是有报错警告
if (process.env.NODE_ENV !== 'production') {
if (typeof reducers[key] === 'undefined') {
warning(`No reducer provided for key "${key}"`)
}
}
// 是function(dispatch)就存入最终的reducer
if (typeof reducers[key] === 'function') {
finalReducers[key] = reducers[key]
}
}
// 拿到处理后的reducer的key
const finalReducerKeys = Object.keys(finalReducers)
// This is used to make sure we don't warn about the same
// keys multiple times.
let unexpectedKeyCache: { [key: string]: true }
// 判断是否生产环境,不是就赋值为空对象
if (process.env.NODE_ENV !== 'production') {
unexpectedKeyCache = {}
}
// 报错
let shapeAssertionError: Error
try {
// 初始化reducer,判断是否合法
assertReducerShape(finalReducers)
} catch (e) {
// 捕获error
shapeAssertionError = e
}
return function combination(
state: StateFromReducersMapObject<typeof reducers> = {},
action: AnyAction
) {
// 如果有error 抛出
if (shapeAssertionError) {
throw shapeAssertionError
}
// 判断是否生产环境,警告信息
if (process.env.NODE_ENV !== 'production') {
const warningMessage = getUnexpectedStateShapeWarningMessage(
state,
finalReducers,
action,
unexpectedKeyCache
)
if (warningMessage) {
warning(warningMessage)
}
}
// 是否改变boolean,两个state
let hasChanged = false
const nextState: StateFromReducersMapObject<typeof reducers> = {}
// 遍历最终的reducer的
for (let i = 0; i < finalReducerKeys.length; i++) {
// 拿key
const key = finalReducerKeys[i]
// 拿每个reducer
const reducer = finalReducers[key]
// 拿state中对应的key
const previousStateForKey = state[key]
// 对state中的key做dispatch
const nextStateForKey = reducer(previousStateForKey, action)
// 如果取到空值 error
if (typeof nextStateForKey === 'undefined') {
const errorMessage = getUndefinedStateErrorMessage(key, action)
throw new Error(errorMessage)
}
// 如果没取到空值就存入新state
nextState[key] = nextStateForKey
// 重新赋值是否改变boolean,判断两个state的key是否相等
hasChanged = hasChanged || nextStateForKey !== previousStateForKey
}
// 再次赋值是否改变boolean,根据最终reducer的key的长度和新state的key长度是否相等
hasChanged =
hasChanged || finalReducerKeys.length !== Object.keys(state).length
// 根据是否改变boolean来选择返回新旧state
return hasChanged ? nextState : state
}
}
- 将传入的多个reducer整合成一个reducer
- 如果在非生产环境发现传入的 reducers 有为空的则抛出错误。
- 将不为空的 reducers 整合到新的集合 finalReducers。
- 如果在非生产环境会对传入的 reducers 尝试性初始化一次,如果发现初始化后返回的 state 是 undefined - 则保存错误,在下面第一次调用 combination(返回的 reducer 函数) 时抛出。
- 返回一个大的 reducer(combination)。
- 当调用了 combination,一般就是 dispatch,createStore 的时候,在非生产环境会先进行合法性校验,如果发现有不合法的地方,抛出错误。
- 然后把传入的 action 传入到每一个子 reducer 里运行,得到新的 state,然后将每个子 state tree 整合起来返回。
bindActionCreators
// 将传入的actionCreator转换成dispatch包裹后的函数
function bindActionCreator<A extends AnyAction = AnyAction>(
actionCreator: ActionCreator<A>,
dispatch: Dispatch
) {
return function (this: any, ...args: any[]) {
return dispatch(actionCreator.apply(this, args))
// 这样应该也行
/**
* return function() {
* return dispatch(actionCreator.apply(this, arguments))
* }
*/
}
}
export default function bindActionCreators(
actionCreators: ActionCreator<any> | ActionCreatorsMapObject,
dispatch: Dispatch
) {
// 判断actionCreators是否为函数,如果是函数就将其和dispatch绑定
if (typeof actionCreators === 'function') {
return bindActionCreator(actionCreators, dispatch)
}
// 判断actionCreators只能是函数和不包括null的对象
if (typeof actionCreators !== 'object' || actionCreators === null) {
throw new Error(
`bindActionCreators expected an object or a function, instead received ${
actionCreators === null ? 'null' : typeof actionCreators
}. ` +
`Did you write "import ActionCreators from" instead of "import * as ActionCreators from"?`
)
}
// 初始化一个对象
const boundActionCreators: ActionCreatorsMapObject = {}
// 遍历actionCreators
for (const key in actionCreators) {
// 拿对应的函数或对象
const actionCreator = actionCreators[key]
// 如果是函数,绑定函数和dispatch
if (typeof actionCreator === 'function') {
boundActionCreators[key] = bindActionCreator(actionCreator, dispatch)
}
}
// 返回绑定dispatch后的 actionCreators对象
return boundActionCreators
}
用于将dispatch和actionCreator绑定
- 遍历actionCreators,将每个actionCreator用dispatch包装
utils
actionTypes
// 生成随机数
const randomString = () =>
Math.random().toString(36).substring(7).split('').join('.')
const ActionTypes = {
// 防止重名
INIT: `@@redux/INIT${/* #__PURE__ */ randomString()}`,
REPLACE: `@@redux/REPLACE${/* #__PURE__ */ randomString()}`,
// 这个每次生成的都不一样
PROBE_UNKNOWN_ACTION: () => `@@redux/PROBE_UNKNOWN_ACTION${randomString()}`
}
export default ActionTypes
加上随机数防止与用户定义的actionType重名
- INIT和REPLACE作用相同,PROBE_UNKNOWN_ACTION每次都会生成一个一次性的随机数
isPlainObject
// 判断是否是普通对象{...}
export default function isPlainObject(obj: any): boolean {
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
}
判断是否为普通对象( {}或new Object()创造的对象 )
- 先排除不是对象的和null
- 再通过原型链判断是否为普通对象:Object.prototype
为什么不用Object.prototype.toString判断? 见:https://www.zhihu.com/question/287632207