Redux 源码解析

175 阅读5分钟

序言

前段时间,大概看了下redux原理,主要是想了解下这个库是怎么处理状态变更的,但是也只是看了个大概。最近,想要深入的理解一下底层的原理,知其然知其所以然,便写了个demo,深入了解了下。

redux项目

最新版本的redux源码已经用typescript重写,还是比较易于阅读的。

redux仓库地址: github.com/reduxjs/red…

如何学习源码

  1. 新建demo仓库,引入redux源码,断点调试,查看整个流程
  1. 查找网上的redux源码分析文章,结合别人的分析加上自己的理解去学习

demo

  • 为了快速搭建项目,使用create-react-app脚手架
npx create-react-app redux-demo --typescript
  • 其他具体细节,可以拉该仓库下来去了解: redux-demo

源码分析

  1. 新建reducer

    1. 创建testInfo-reducer
      const INITIAL_STATE = {
        display: true
      };
      
      export function testInfo(state = INITIAL_STATE, action: any) {
        switch (action.type) {
          case 'update':
            state = {
              display: action.data
            };
            break;
        }
        return state;
      }
      
    2. 创建reducers文件
      import { combineReducers } from 'redux'
      import { testInfo } from './testInfo';
      export default combineReducers({ testInfo })
      

      combineReducers做了什么?

      function assertReducerShape(reducers: ReducersMapObject) {
        Object.keys(reducers).forEach(key => {
          const reducer = reducers[key]
          const initialState = reducer(undefined, { type: ActionTypes.INIT })
      
          if (typeof initialState === 'undefined') {
            throw new Error(
              `The slice reducer for key "${key}" returned undefined during initialization. ` +
              `If the state passed to the reducer is undefined, you must ` +
              `explicitly return the initial state. The initial state may ` +
              `not be undefined. If you don't want to set a value for this reducer, ` +
              `you can use null instead of undefined.`
            )
          }
      
          if (
            typeof reducer(undefined, {
              type: ActionTypes.PROBE_UNKNOWN_ACTION()
            }) === 'undefined'
          ) {
            throw new Error(
              `The slice reducer for key "${key}" returned undefined when probed with a random type. ` +
              `Don't try to handle '${ActionTypes.INIT}' or other actions in "redux/*" ` +
              `namespace. They are considered private. Instead, you must return the ` +
              `current state for any unknown actions, unless it is undefined, ` +
              `in which case you must return the initial state, regardless of the ` +
              `action type. The initial state may not be undefined, but can be null.`
            )
          }
        })
      }
      
      export default function combineReducers(reducers: ReducersMapObject) {
        // 获取传入 reducers 对象集合的key值,此处我们传入的对象是 {'testInfo': testInfo},
        // 因此得到的 reducerKeys = ['textInfo'];
        const reducerKeys = Object.keys(reducers)
        // 由于传入的reducer必须是Function,因此需要做一层过滤
        const finalReducers: ReducersMapObject = {}
        for (let i = 0; i < reducerKeys.length; i++) {
          const key = reducerKeys[i]
      
          // 判断当前的reducer是否是function,是的话,存入finalReducers的缓对象集合中
          if (typeof reducers[key] === 'function') {
            finalReducers[key] = reducers[key]
          }
        }
        // 最终得到的 finalReducerKeys = {'testInfo': testInfo}
        // 获取最终生成的reducer的key值集合,此处是['testInfo']
        const finalReducerKeys = Object.keys(finalReducers)
      
        let shapeAssertionError: Error
        try {
          // 此处可以理解为对reducer做了一层校验,确保每个reducer的初始值均不是undefined
          assertReducerShape(finalReducers)
        } catch (e) {
          shapeAssertionError = e as Error
        }
      
        // 最终生成一个combination函数,入参是state & action
        return function combination(state: StateFromReducersMapObject<typeof reducers> = {},action: AnyAction) {
          if (shapeAssertionError) {
            throw shapeAssertionError
          }
      
          // 标记执行完所有的reducer之后,状态有没有发生变化,如果有,需要返回变更之后新的state,否则返回旧的state即可
          let hasChanged = false
          const nextState: StateFromReducersMapObject<typeof reducers> = {}
          // 根据reducer的key值,来依次执行所有的reducer
          for (let i = 0; i < finalReducerKeys.length; i++) {
            const key = finalReducerKeys[i]
            // 取到当前需要执行的reducer函数
            const reducer = finalReducers[key]
            // 获取当前reducer变更之前的值
            const previousStateForKey = state[key]
            // 执行reducer函数,入参是之前的值,以及当前可能需要发生变化的行为,获取reducer执行之后的值
            const nextStateForKey = reducer(previousStateForKey, action)
            // 如果执行之后的值是undefined,说明传入的action提供的值异常
            if (typeof nextStateForKey === 'undefined') {
              const actionType = action && action.type
              throw new Error(
                `When called with an action of type ${actionType ? `"${String(actionType)}"` : '(unknown type)'
                }, the slice reducer for key "${key}" returned undefined. ` +
                `To ignore an action, you must explicitly return the previous state. ` +
                `If you want this reducer to hold no value, you can return null instead of undefined.`
              )
            }
            // 添加新的状态值到nextState中
            nextState[key] = nextStateForKey
            // 比较当前值与之前的值是否相等,不相等,则标记hasChanged发生改变
            hasChanged = hasChanged || nextStateForKey !== previousStateForKey
          }
          
          // 执行完所有的reducer之后,判断下是否发生变化
          hasChanged = hasChanged || finalReducerKeys.length !== Object.keys(state).length;
          // 发生变化,返回新的状态对象,否则返回旧值
          return hasChanged ? nextState : state
        }
      }
      
  1. 创建store

    1. 创建store文件

      import { createStore, applyMiddleware, compose } from 'redux';
      import rootReducer from './reducers'
      // 为了说明中间件的注册流程,引入redux-logger插件
      import { createLogger } from 'redux-logger';
      
      const middlewares: never[] = []
      
      if (process.env.NODE_ENV === 'development' && process.env.TARO_ENV !== 'quickapp') {
        // 开发环境注入
        middlewares.push(createLogger() as unknown as never)
      }
      
      // 生成中间件function集合
      const middlewareFuncs = applyMiddleware(middlewares);
      
      // 包装生成最终的function
      const enhancer = compose(middlewareFuncs);
      
      // 创建store
      const store = createStore(rootReducer, enhancer);
      
      export { store };
      
      1. applyMiddleware生成了什么?
      // 基础action,仅包含操作的type
      export interface Action<T = any> {
        type: T
       }
      
       // 扩充的action,包含除了type之外的扩充字段,一般习惯用action = {type: 'xxx', data: {}}
       export interface AnyAction extends Action {
         [extraProps: string]: any
       }
      
       // dispatch函数声明可以理解为type dispatch = (action: AnyAction, ...extraArgs:any[])=>AnyAction;
       export interface Dispatch<A extends Action = AnyAction> {
         <T extends A>(action: T, ...extraArgs: any[]): T
       }
      
       // 中间件API类型,必须包含dispatch & getState方法
       export interface MiddlewareAPI<D extends Dispatch = Dispatch, S = any> {
         dispatch: D
         getState(): S
       }
      
       // 中间件类型,约定该函数是(api: MiddlewareAPI): ((next: D) => (action: D extends Dispatch<infer A> ? A : never) => any)
       export interface Middleware<_DispatchExt = {}, S = any, D extends Dispatch = Dispatch> {
         (api: MiddlewareAPI<D, S>): (next: D) => (action: D extends Dispatch<infer A> ? A : never) => any
       }
      
      
       // 接收的参数是一个
       export default function applyMiddleware(middlewares: Middleware[]): StoreEnhancer {
         // 执行完applyMiddleware之后,实际上返回了一个function,该函数的入参是createStore【详见store定义】
         // 返回值是入参 (reducer: Reducer<S, A>, preloadedState?: PreloadedState<S>),返回值是{...store和合成之后的dispatch}
         return (createStore: StoreEnhancerStoreCreator) => <S, A extends AnyAction>(reducer: Reducer<S, A>, preloadedState?: PreloadedState<S>) => {
           // 创建store
           const store = createStore(reducer, preloadedState)
           // 定义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)
           }
      
           // 依次执行注入的中间件数组,生成新的 (next: D) => (action: D extends Dispatch<infer A> ? A : never) => any 函数结合
           const chain = middlewares.map(middleware => middleware(middlewareAPI));
           // 生成新的dispath,这些dispatch已经包裹了所有的中间件,生成了新的dispatch
           dispatch = compose<typeof dispatch>(...chain)(store.dispatch)
      
           // 生成新的store
           return {
             ...store,
             dispatch
           }
         }
       }
      
      1. compose生成了什么?
      type Func<T> = (...args: any[]) => T;
      // 入参是一个函数数组集合
      export default function compose<T>(...funcs: Func<T>[]): Func<T> {
        if (funcs.length === 0) {
          return (arg: T) => arg
        }
      
        if (funcs.length === 1) {
          return funcs[0]
        }
         
        // 不做解释,自己看下reduce
        return funcs.reduce((a, b) => (...args: any) => a(b(...args)))
      }
      
      1. createStore生成了什么?
      // reducer是combineReducers之后生成的(state: any, action:any)=> state:any
      // PreloadedState为可选参数,是一个对象,为state的初始状态,一般不传
      // enhancer 一般为中间件compose之后的function: createStore => (reducer, preloadedState) => {...}
      export default function createStore<State, A extends Action, Ext = {}, StateExt = never>(reducer: Reducer<State, A>, preloadedState?: PreloadedState<State> | StoreEnhancer<Ext, StateExt>, enhancer?: StoreEnhancer<Ext, StateExt>): Store<ExtendState<State, 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. See https://redux.js.org/tutorials/fundamentals/part-4-store#creating-a-store-with-enhancers for an example.'
          )
        }
        
        if (typeof preloadedState === 'function' && typeof enhancer === 'undefined') {
          enhancer = preloadedState as StoreEnhancer<Ext, StateExt>
          preloadedState = undefined
        }
        if (typeof enhancer !== 'undefined') {
          if (typeof enhancer !== 'function') {
            throw new Error(
              `Expected the enhancer to be a function. Instead, received: '${kindOf(
                enhancer
              )}'`
            )
          }
          
          // 外部调用完一下代码之后,创建createStore的流程
          // const middlewareFuncs = applyMiddleware(middlewares);
          // const enhancer = compose(middlewareFuncs);
          // const store = createStore(rootReducer, enhancer);
          // enhancer是中间件合成之后的function,入参为createStore本身,执行之后,返回:(reducer: Reducer<S, A>, preloadedState?: PreloadedState<S>)=> store 
          // 再次执行(reducer: Reducer<S, A>, preloadedState?: PreloadedState<S>)=> store,返回创建的store,此时的store.dispatch已经被中间件包裹,生成新的function
          return enhancer(createStore)(reducer, preloadedState as PreloadedState<State>) as Store<ExtendState<State, StateExt>, A, StateExt, Ext> & Ext
        }
      
        if (typeof reducer !== 'function') {
          throw new Error(
            `Expected the root reducer to be a function. Instead, received: '${kindOf(
              reducer
            )}'`
          )
        }
         // return enhancer(createStore)(reducer, preloadedState as PreloadedState<State>) as Store<ExtendState<State, StateExt>, A, StateExt, Ext> & Ext这个function调用里,会调用createStore方法,详见applyMiddleware实现
        // 初始化当前的reducer【合成之后】
        let currentReducer = reducer
        // 初始化当前状态,默认是undefined
        let currentState = preloadedState as State
        // 初始化当前监听者【主要用于store里边的state发生变化时,通知相关的监听者】
        let currentListeners: (() => void)[] | null = []
        let nextListeners = currentListeners
        let isDispatching = false
      
        function ensureCanMutateNextListeners() {
          // 如果当前的nextListeners & currentListeners指向的是同一个引用,则生成新的拷贝,并赋值给nextListeners
          if (nextListeners === currentListeners) {
            nextListeners = currentListeners.slice()
          }
        }
      
        function getState(): State {
          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 as State
        }
      
        function subscribe(listener: () => void) {
          if (typeof listener !== 'function') {
            throw new Error(
              `Expected the listener to be a function. Instead, received: '${kindOf(
                listener
              )}'`
            )
          }
      
          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.'
            )
          }
      
          let isSubscribed = true
          // 生成新的nextListeners
          ensureCanMutateNextListeners()
          // 此时nextListeners的变更不影响currentListeners
          nextListeners.push(listener)
      
          // 返回值是一个取消订阅的api
          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/store#subscribelistener for more details.'
              )
            }
            isSubscribed = false
            // 生成新的nextListeners,nextListeners的变化不影响currentListeners
            ensureCanMutateNextListeners()
            // 找到需要移除的listener,并移除
            const index = nextListeners.indexOf(listener)
            nextListeners.splice(index, 1)
            // 重置当前的currentListeners集合
            currentListeners = null
          }
        }
      
        function dispatch(action: A) {
          // 要求传入的action必须是一个字面量对象
          if (!isPlainObject(action)) {
            throw new Error(
              `Actions must be plain objects. Instead, the actual type was: '${kindOf(
                action
              )}'. You may need to add middleware to your store setup to handle dispatching other values, such as 'redux-thunk' to handle dispatching functions. See https://redux.js.org/tutorials/fundamentals/part-4-store#middleware and https://redux.js.org/tutorials/fundamentals/part-6-async-logic#using-the-redux-thunk-middleware for examples.`
            )
          }
      
          // 要求传入的action必须有type字段,且不为undefined
          if (typeof action.type === 'undefined') {
            throw new Error(
              'Actions may not have an undefined "type" property. You may have misspelled an action type string constant.'
            )
          }
      
          if (isDispatching) {
            throw new Error('Reducers may not dispatch actions.')
          }
      
          try {
            isDispatching = true
            // 调用所有的reducer生成新的state对象
            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
        }
      
        function replaceReducer<NewState, NewActions extends A>(nextReducer: Reducer<NewState, NewActions>): Store<ExtendState<NewState, StateExt>, NewActions, StateExt, Ext> & Ext {
          if (typeof nextReducer !== 'function') {
            throw new Error(
              `Expected the nextReducer to be a function. Instead, received: '${kindOf(
                nextReducer
              )}`
            )
          }
      
          (currentReducer as unknown as Reducer<NewState, NewActions>) = nextReducer
      
          dispatch({ type: ActionTypes.REPLACE } as A)
      
          return store as unknown as Store<ExtendState<NewState, StateExt>, NewActions, StateExt, Ext> & Ext
        }
      
        // 一般用不到
        function observable() {
          const outerSubscribe = subscribe
          return {
            subscribe(observer: unknown) {
              if (typeof observer !== 'object' || observer === null) {
                throw new TypeError(
                  `Expected the observer to be an object. Instead, received: '${kindOf(
                    observer
                  )}'`
                )
              }
      
              function observeState() {
                const observerAsObserver = observer as Observer<State>
                if (observerAsObserver.next) {
                  observerAsObserver.next(getState())
                }
              }
      
              observeState()
              const unsubscribe = outerSubscribe(observeState)
              return { unsubscribe }
            },
            [$$observable]() {
              return this
            }
          }
        }
      
        // 创建的时候,做一次初始化操作
        dispatch({ type: ActionTypes.INIT } as A)
      
        const store = {
          dispatch: dispatch as Dispatch<A>,
          subscribe,
          getState,
          replaceReducer,
          [$$observable]: observable
        } as unknown as Store<ExtendState<State, StateExt>, A, StateExt, Ext> & Ext
        return store
      }
      
  1. 使用store,并触发变更

    1. 新建storeTest文件,并添加store的事件监听

      ```
      import { store } from './store';
      
      const listener1 = store.subscribe(() => {
        console.error('listener1 getState:', store.getState());
      })
      
      const listener2 = store.subscribe(() => {
        console.error('listener2 getState:', store.getState());
      })
      
      const listener3 = store.subscribe(() => {
        console.error('listener3 getState:', store.getState());
      })
      ```
      
    2. 触发store变更

      setInterval(() => {
        store.dispatch({
          type: 'update',
          data: Date.now()
        })
      })
      

总结

  • redux核心代码还是比较少的,作者非常的巧妙的运用了闭包和函数柯里化。初读确实会有些晦涩难懂,再看不禁要发出赞叹,写的太好了。

参考

结尾

感谢你在百忙之中阅读了此文章,我是今日可乐,我在字节,我爱技术。

ps 欢迎有志之士投递:job.toutiao.com/s/LwVE3jt

欢迎关注本人公众号:

qrcode_for_gh_f3e01d7316c6_258.jpg