Redux 源码学习

想着对 React 生态更深刻的学习,何况 React 源码内容也很少的状况,来读一下他的源码内容。 目前 Redux 已是 TypeScript 版本的源码,我正在阅读的则是 4.0.4 版本代码。typescript

很显然,能够看到这和 React 仓库同样,一样使用了 rollup 打包工具,也验证了 rollup 在开发库类仓库的优点,毕竟大厂都在用嘛。json

咱们先来看到 package.jsonredux

{
  "name": "redux",
  "version": "4.0.4",
  "main": "lib/redux.js",
  "unpkg": "dist/redux.js",
  "module": "es/redux.js",
  "types": "types/index.d.ts",
}
复制代码

我省略了大部份内容,能够看到开发团队针对不一样使用场景,对外提供包对应的路径,好比使用 TypeScript 时,对应的则是 types/index.d.ts ,或者使用 ES6 模块化引入时则是 es/redux.js 。值得咱们学习。api

接下来,咱们看到源码部分,整个源码学习内容,将围绕着 src 进行, utils 下的内容即一些简单的工具, types 下则是一些类型的定义。数组

示例代码中我会移除并省略掉一些逻辑代码和 TypeScript 相关的函数重载代码,以及一些我认为不那么重要的代码,即只展示我想要说明的内容。markdown

那么咱们先来看到 createStore 函数吧。app

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 {
  // 接受的 reducer
  let currentReducer = reducer
  // 预加载 state
  let currentState = preloadedState as S
  // 订阅队列
  let currentListeners: (() => void)[] | null = [] // 下一个订阅队列,看起来更像是订阅队列的副本,下面我会来介绍他和订阅队列的不解之谜 let nextListeners = currentListeners // 检查并建立这个副本的函数 function ensureCanMutateNextListeners() { if (nextListeners === currentListeners) { // 拷贝到一个新的数组引用,能够理解为 [...currentListeners] 使得 nextListeners !== currentListeners nextListeners = currentListeners.slice() } } // 得到 state function getState(): S { return currentState as S } // 订阅函数 function subscribe(listener: () => void) { ensureCanMutateNextListeners() // 操做订阅队列副本,与 currentListeners 之间不受影响 nextListeners.push(listener) // 返回取消订阅函数 return function unsubscribe() { ensureCanMutateNextListeners() // 一样的效果 const index = nextListeners.indexOf(listener) nextListeners.splice(index, 1) currentListeners = null } // 看到这里你是否是会想,建立这个副本的目的是什么? // 官方给到的解释:This prevents any bugs around consumers calling subscribe/unsubscribe in the middle of a dispatch. } // dispatch 函数 function dispatch(action: A) { currentState = currentReducer(currentState, action) // 遍历通知订阅队列,将 currentListeners 赋值为“最新”的队列 const listeners = (currentListeners = nextListeners) for (let i = 0; i < listeners.length; i++) { const listener = listeners[i] listener() } return action } // 替换 reducer 函数 function replaceReducer<NewState, NewActions extends A>( nextReducer: Reducer<NewState, NewActions> ): Store<ExtendState<NewState, StateExt>, NewActions, StateExt, Ext> & Ext { // 更新 reducer ;((currentReducer as unknown) as Reducer< NewState, NewActions >) = nextReducer // 同初始化功能一致,即生成 currentState ,下面代码中会说起 dispatch({ type: ActionTypes.REPLACE } as A) return (store as unknown) as Store< ExtendState<NewState, StateExt>, NewActions, StateExt, Ext > & Ext } // 初始化,生成 currentState ,即调用 getState 函数返回的内容, ActionTypes.INIT 便可以理解为私有类型,不会影响到你本身写的 reducer 毕竟里面包含了随机数... dispatch({ type: ActionTypes.INIT } as A) // 暴露的 store 对象 const store = ({ dispatch: dispatch as Dispatch<A>, subscribe, getState, replaceReducer, // 观察者方法这里不作解读,能够理解为对 subscribe 的封装 [?observable]: observable } as unknown) as Store<ExtendState<S, StateExt>, A, StateExt, Ext> & Ext return store } 复制代码

那么到此,咱们看到了整个 createStore 函数的实现过程以及返回的内容。模块化

这里我来简单的代过 compose 函数,也是业界的经常使用实现,不是很明白其功能的同窗能够简单看到这段代码便可:函数

(funcs as : Function[]).reduce((a, b) => (...args: any) => a(b(...args)))
复制代码

接下来,咱们来看看 combineReducers 的实现,你能够把这个函数理解为对 reducer 的合并,固然函数名就是这个意思。工具

combineReducers

export default function combineReducers(reducers: ReducersMapObject) {
  const reducerKeys = Object.keys(reducers)
  // 用于 combination 函数中使用所需,收集的有效 reducers
  const finalReducers: ReducersMapObject = {}
  for (let i = 0; i < reducerKeys.length; i++) {
    const key = reducerKeys[i]

    if (typeof reducers[key] === 'function') {
      finalReducers[key] = reducers[key]
    }
  }
  // 收集的 keys
  const finalReducerKeys = Object.keys(finalReducers)

  return function combination( state: StateFromReducersMapObject<typeof reducers> = {}, action: AnyAction ) {
    let hasChanged = false
    // 新的 state
    const nextState: StateFromReducersMapObject<typeof reducers> = {}
    // 遍历 keys
    for (let i = 0; i < finalReducerKeys.length; i++) {
      const key = finalReducerKeys[i]
      const reducer = finalReducers[key]
      const previousStateForKey = state[key]
      // 从遍历中更新 state
      const nextStateForKey = reducer(previousStateForKey, action)
      nextState[key] = nextStateForKey
      hasChanged = hasChanged || nextStateForKey !== previousStateForKey
    }
    hasChanged =
      hasChanged || finalReducerKeys.length !== Object.keys(state).length
    // 返回 state 或新的 state
    return hasChanged ? nextState : state
  }
}
复制代码

在咱们看完 reducer 的组合工具函数后,咱们来看到绑定执行上下文的语法糖函数 bindActionCreators

bindActionCreators

实现代码不多,即绑定执行上下文环境 this 。具体使用场景,能够看到官网 API 文档 bindActionCreators(actionCreators, dispatch) ,那么直接看到源码:

function bindActionCreator<A extends AnyAction = AnyAction>( actionCreator: ActionCreator<A>, dispatch: Dispatch ) {
  return function(this: any, ...args: any[]) {
    return dispatch(actionCreator.apply(this, args))
  }
}

export default function bindActionCreators( actionCreators: ActionCreator<any> | ActionCreatorsMapObject, dispatch: Dispatch ) {
  if (typeof actionCreators === 'function') {
    return bindActionCreator(actionCreators, dispatch)
  }

  const boundActionCreators: ActionCreatorsMapObject = {}
  for (const key in actionCreators) {
    const actionCreator = actionCreators[key]
    if (typeof actionCreator === 'function') {
      boundActionCreators[key] = bindActionCreator(actionCreator, dispatch)
    }
  }
  return boundActionCreators
}
复制代码

最后,咱们来看到中间件函数 applyMiddleware

applyMiddleware

export default function applyMiddleware( ...middlewares: Middleware[] ): StoreEnhancer<any> {
  return (createStore: StoreCreator) => <S, A extends AnyAction>(
    reducer: Reducer<S, A>,
    ...args: any[]
  ) => {
    const store = createStore(reducer, ...args)
    let dispatch: Dispatch = () => {
      throw new Error('...')
    }

    const middlewareAPI: MiddlewareAPI = {
      getState: store.getState,
      dispatch: (action, ...args) => dispatch(action, ...args)
    }
    const chain = middlewares.map(middleware => middleware(middlewareAPI))
    // 对 store.dispatch 进行改写,将其包装在中间件的最后一层,以实现中间件的功能
    dispatch = compose<typeof dispatch>(...chain)(store.dispatch)

    return {
      ...store,
      dispatch
    }
  }
}
复制代码

OK, Redux 源码学习完了,是否是没看够,确实!

这才是精妙的库设计,简洁的代码实现强大的功能,那么此次没过瘾不要紧,下次咱们来品一品 Redux 是如何和 React 结合在一块儿的吧。