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

© kaba 2019 - 2023