Redux 源码阅读

126 阅读7分钟

Redux 源码阅读

  • V5.0.0 Beta
  • 第一次阅读源码

createStore

Redux 核心逻辑就是createStore 代码非常简单

function createStore(reducer, preloadedState, enhancer){
  // 一些判断,保证参数正确

  /* 定义的变量,后面定义的函数都可以访问这些变量 */

  // reducer函数
  let currentReducer = reducer
  // 状态: store中的状态都保存在这里
  let currentState = preloadedState
  // 调用subscribe后,就存在这里
  let currentListeners = new Map()
  let nextListeners = currentListeners
  let listenerIdCounter = 0
  // 标识是不是正在执行dispatch()
  // true状态下,不能够再dispatch, 例如reducer函数中dispatch。
  // true状态下,不能subscribe/unsubscribe,不能getState
  let isDispatching = false

  /* 主要的的函数 */

  function getState(){
    return currentState
  }

  function subscribe(listerner){
    // dispatch subscribe 实现了发布订阅模式
  }

  function dispatch(action){
    isDispatching = true
    // 计算下一个状态
    currentState = currentReducer(currentState, action)
    isDispatching = false
    const listeners = (currentListeners = nextListeners)
    // 触发listener
    listeners.forEach(listener => {
      listener()
    })
  }

  function replaceReducer(){
    // 替换reducer
  }

  function observable(){}
  // 初始化:从reducer收集状态
  dispatch({type: ActionTypes.INIT})

  const store {
    ...
  }
  return store
}

applyMiddleware

applyMiddleware是 Redux 里面最难理解的一部分了

enhancer

applyMiddleware实际上是一个 enhancer 工厂函数,先了解enhancerenhancer 是一个函数,故名思意是用来增强 createStore 的

大概长这样

function enhancer(createStore) {
  return function myCreateStore(reducer, preloadedState) {
    // 做些什么
    const store = createStore()
    return store
  }
}
// 箭头函数的写法
const enhancer = createStore => (reducer, preloadedState) => {
  // 做些什么
  const store = createStore()
  return store
}

applyMiddleware

好了,继续研究applyMiddleware

applyMiddleware是高阶函数,返回 enhancer 源码如下:

export default function applyMiddleware(
  ...middlewares: Middleware[]
): StoreEnhancer<any> {
  return createStore => (reducer, preloadedState) => {
    const store = createStore(reducer, preloadedState)
    // 4.0之前是  let dispatch = store.dispatch
    let dispatch: Dispatch = () => {
      throw new Error(
        'Dispatching while constructing your middleware is not allowed. ' +
          'Other middleware would not be applied to this dispatch.'
      )
    }
      // midelewareAPI 实际上是个mini store
    const middlewareAPI: MiddlewareAPI = {
      getState: store.getState,
      // 包裹一层函数, 形成一个闭包
      dispatch: (action, ...args) => dispatch(action, ...args)
    }
    const chain = middlewares.map(middleware => middleware(middlewareAPI))
    // 最终的dispatch
    dispatch = compose<typeof dispatch>(...chain)(store.dispatch)

    return {
      ...store,
      dispatch
    }
  }
}


dispatch: (action, ...args) => dispatch(action, ...args), 这里用的非常巧妙,用闭包捕获 dispatch, 这样可以保证 所有 middleware 都能获取到最终的 dispatch。 有延迟的味道了,虽然传参数的时候 dispatch 就是个抛出异常的函数,但是真正要执行的时候,已经替换成最终的 dispatch

image-3.png

为了方便理解可以隐藏细节,简化一下applyMiddleware

function applyMiddleware(middlewares) {
  return function enhancer(createStore) {
    return myCreateStore(reducer, preloadedState){
      // 做些社么
    }
  }
}


function applyMiddleware(middlewares){
  return (createStore)=>(reducer, preloadedState)=>{...}
}

什么是 Middleware

image.png

看一下(reducer, preloadedState)=>{...},这里处理传入的 middlewares

先看一下 Middleware 定义(简化)

interface Middleware<
  S = any,
  D extends Dispatch = Dispatch
> {
  (api: MiddlewareAPI<D, S>): (
    next: (action: unknown) => unknown
  ) => (action: unknown) => unknown
}
// 就是一个mini store
interface MiddlewareAPI<D extends Dispatch = Dispatch, S = any> {
  dispatch: D
  getState(): S
}

Middleware 接受一个 MiddlewareAPI(mini store), 返回一个函数, 这里命名为dispatchEnhancer

dispatchEnhancer签名如下: 注意: dispatchEnhancer是我自己为了方便阅读增加的概念,源码里面没有这玩意

type DipatchEnhancer = (
  next: (action: unknown) => unknown
) => (action: unknown) => unknown
// 为了更好理解, 可以简化成
type D = (action: unkion) => unknown
type DispatchEnhancer = (next: D) => D

dispatchEnhancer是一个高阶函数, 参数名为next的函数,然后返回一个函数: 很容易注意到:形参和返回值的类型都是(action: unknown)=> unknown

然后这个类型和最原始的dispatch类型是差不多的。得出以下结论:

  • next 就是一个 dispatch 函数,可能是最原始,也可能是其它中间件
  • 返回值也是个 dispatch 函数
  • 这也是为什么我把这个函数命名为dispatchEnhancer, 它和createStoreenhancer类似

处理传入的 middlewares

const chain = middlewares.map(middleware => middleware(middlewareAPI))
dispatch = compose<typeof dispatch>(...chain)(store.dispatch)

第 1 行把middlewareAPI传入所有 middleware 中,得到的 chain 就是一个DispatchEnhancer[],例如[loggerDisptchEnhancer, otherDispatchEnhancer ]

第 2 行先使用compose把所有dispatchEncher复合起来

const foo = compose(f, g, h)(...arg)
// 等价于
const foo = f(g(h(...arg)))

image.png 因此第 2 行可以理解为:

const dispatch = loggerDispatchEnhancer(otherDispatchEnhance(store.dispatch))

最终,我们在组件里调用的就是经过 middlewares 层层加工的dispatch

看个例子

const logger_1: Middleware = store => {
  return next => {
    return function loggerDispatch_1(action) {
      console.log('dispatching', action)
      let result = next(action)
      console.log('next state', store.getState())
      return result
    }
  }
}

const logger_2: Middleware = store => {
  return next => {
    return function loggerDispatch_2(action) {
      console.log('dispatching', action)
      let result = next(action)
      console.log('next state', store.getState())
      return result
    }
  }
}
const store = createStore(
  reducer,
  { value: 0 },
  applyMiddleware(logger_1, logger_2)
)
store.dispatch({ type: 'ADD' })

store.disaptch(...)的时候,执行栈如下 image-1.png

image.png

为什么这样设计?

下面都是自己的理解,可能有误

假设没有 redux 没有提供applyMiddleware。要实现类似的功能,可以写两个 enhancer:logger, otherEnhancer

const logger: StoreEnhancer = createStore => (reducer, state) => {
  const store = createStore(reducer, state)
  const dispatch = store.dispatch

  const dispatchA: typeof dispatch = action => {
    console.log('dispatchA', action)
    return dispatch(action)
  }

  return {
    ...store,
    dispatch: dispatchA
  }
}

const otherEnhancer: StoreEnhancer = createStore => (reducer, state) => {
  const store = createStore(reducer, state)
  const dispatch = store.dispatch

  const dispatchB: typeof dispatch = action => {
    console.log('dispatchB', action)
    return dispatch(action)
  }

  return {
    ...store,
    dispatch: dispatchB
  }
}

const store = createStore(reducer, { value: 0 }, compose(logger, otherEnhancer))
store.dispatch({ type: 'ADD' })
console.log(store.getState().value)

/* 输出如下
dispatchA {type: 'ADD'}
dispatchB {type: 'ADD'}
{value: 1}
*/

提炼出 myApplyMiddleware

这样完美实现了我们的要求,但是有很多重复的模板代码,显然可以提炼出myApplyMiddleware

function myApplyMiddleware(...middlewares: Function[]): StoreEnhancer {
  return createStore => (reducer, preloadedState) => {
    const store = createStore(reducer, preloadedState)
    // 这里做些什么
    return {
      ...store,
      dispatch
    }
  }
}

const store = createStore(
  reducer,
  { value: 0 },
  myApplyMidlleware(loggerA, loggerB)
)

设计 middleware

现在的问题是 logger, otherMiddleware 怎么设计?我们把重复代码提炼成myApplyMiddleware, middleware 显然就是非公共部分了, 观察前面的enhancer:

const otherEnhancer: StoreEnhancer = createStore => (reducer, state) => {
  const store = createStore(reducer, state)
  const dispatch = store.dispatch
  // =====start=====
  const dispatchB: typeof dispatch = action => {
    console.log('dispatchB', action)
    return dispatch(action)
  }
  // =====end=====
  return {
    ...store,
    dispatch: dispatchB
  }
}

根据上面的代码,我们马上想到middleware应该返回一个dispatch, 于是

function loggerMiddleware(store) {
  return function dispatchA(action) {
    console.log('dispatchA', action)
    const result = store.dispatch(action)
    return result
  }
}

function otherMiddleware(store) {
  return function dispatchB(action) {
    console.log('dispatchB', action)
    const result = store.dispatch(action)
    return result
  }
}

此时我们的myApplymiddleware

function myApplyMiddleware(...middlewares): StoreEnhancer {
  return createStore => (reducer, preloadedState) => {
    const store = createStore(reducer, preloadedState)

    const chain = middlewares.map(middleware => middleware(store))
    const dispatch = compose(...chain)

    return {
      ...store,
      dispatch
    }
  }
}

const store = createStore(
  reducer,
  { value: 0 },
  myApplyMidlleware(loggerMiddleware, otherMiddleware)
)
store.dispatch({ type: 'ADD' })
console.log(store.getState().value)

问题在哪里

一切看似完美,甚至觉得自己设计的 API 比原来的更优雅。然而运行结果显示我想多了:

dispatchB {type: 'ADD'}
dispatchA {type: 'ADD'}
{value: 2}

执行顺序和我们预料中的不一样就算了,结果也是错的。

  • 顺序为什么是错的:

因为我们的 middleware 返回的是dispatch,即 dispatchA, dispatchB,

compose(dispathA, dispatchB)(action)

等价于

dispathc(dispatchB(action))

因此执行顺序错了

  • 结果为什么是错的 看下我们的middleware, 直接调用了store.dispatch
function otherMiddleware(store) {
  return function dispatchB(action) {
    console.log('dispatchB', action)
    // =====start=====
    const result = store.dispatch(action)
    // =====end=======
    return result
  }
}

正确的做法是最原始的store.dispatch只会调用一次, 显然不能在中间件里面调用直接store.dispatch

再观察一下前面写的enhancer.

const otherEnhancer: StoreEnhancer = createStore => (reducer, state) => {
  const store = createStore(reducer, state)
  const dispatch = store.dispatch

  const dispatchB: typeof dispatch = action => {
    console.log('dispatchB', action)
    return dispatch(action)
  }

  return {
    ...store,
    dispatch: dispatchB
  }
}
// 等价于
const otherEnhancer: StoreEnhancer = createStore =>
  function otherCreateStore(reducer, state) {
    const store = createStore(reducer, state)
    const dispatch = store.dispatch

    const dispatchB: typeof dispatch = action => {
      console.log('dispatchB', action)
      return dispatch(action)
    }

    return {
      ...store,
      dispatch: dispatchB
    }
  }
newCreateStore = compose(logger, otherEnhancer)(createStore)
// 等价于
newCreateStore = logger(otherEnhancer(createStore))
// 递归执行
newCreateStore(reducer, preloadedState)
                                               -> otherCreateStore(...)
                                                                                             -> original createStore(...)
 <- {...store, dispatch:dispatchA}             <- {...store, dispatch:dispatchB}             <- store
  • enhancer接受一个createStore返回一个新的createStore, 然后再 compose 了一组enhancer
  • 每个 enhancer 返回的createStore, 通过递归调用

而我们middleware返回dispatch, 然后 compose 了一组dispatch

function loggerMiddleware(store) {
  return function dispatchA(action) {
    console.log('dispatchA', action)
    const result = store.dispatch(action)
    return result
  }
}

function otherMiddleware(store) {
  return function dispatchB(action) {
    console.log('dispatchB', action)
    const result = store.dispatch(action)
    return result
  }
}
// myApplyMiddleware
const chain = middlewares.map(middleware => middleware(store))
const dispatch = compose(...chain)

// main.js
const store = createStore(
  reducer,
  { value: 0 },
  myApplyMidlleware(loggerMiddleware, otherMiddleware)
)
store.dispatch({ type: 'ADD' })
console.log(store.getState().value)
// 期望的调用: 递归
dispatch(action):
                  dispatchA(action)
                                    -> dispatchB(action)
                                                        -> original dispatch(action)
                  <-                <-                  <-

// 实际的调用:线性
dispatch(action):
                  dispatchA(dispatchB(action))

dispatch(action):
                  dispatchB(action) -> original dispatch(action)
                  dispatchA(action) -> original dispatch(action)

重新设计 middleware

根据我们前面的观察,可以得出以下结论,

  • middleware不能直接返回dispatch, 因为它的类型是(action: T)=> T
  • 应该 compose funtion(dispatch){return newDispatch},达到递归调用的目的 那么
function loggerMiddleware(store) {
  return next =>
    function dispatchA(action: any) {
      console.log('dispatchA', action)
      return next(action)
    }
}

function otherMiddleware(store) {
  return next =>
    function dispatchB(action: any) {
      console.log('dispatchB', action)
      return next(action)
    }
}

next就是dispatch

同时,myApplyMiddleware也要进行修改

function myApplyMiddleware(...middlewares: Function[]): StoreEnhancer {
  return createStore => (reducer, preloadedState) => {
    const store = createStore(reducer, preloadedState)
    const chain = middlewares.map(middleware => middleware(store))
    const dispatch = compose(...chain)(store.dispatch)
    return {
      ...store,
      dispatch
    }
  }
}
dispatchA {type: 'ADD'}
dispatchB {type: 'ADD'}
{value: 1}

image-2.png

总结

通常我们要递归调用都是要硬编码的:

function foo() {
  console.log('foo enter')
  bar()
  console.log('foo exit')
}
function bar() {
  console.log('bar enter')
  baz()
  console.log('bar exit')
}
function baz() {
  console.log('baz enter')
  console.log('baz exit')
}

但是如果这几个函数都不是我们能控制的,我们要怎么设计才能达到递归调用?

代码如下:

function fooWrapper(next) {
  return function foo() {
    console.log('foo enter')
    next()
    console.log('foo exit')
  }
}

function barWrapper(next) {
  return function bar() {
    console.log('bar enter')
    next()
    console.log('bar exit')
  }
}

function bazWrapper(next) {
  return function baz() {
    console.log('baz enter')
    next()
    console.log('baz exit')
  }
}
compose(fooWrapper, barWrapper, bazWrapper)(() => {})()
// 更改调用顺序, 灵活
compose(barWrapper, bazWrapper, fooWrapper)(() => {})()

第一次阅读源码

收藏夹放了一堆经典必读源码,好几年也没动过,因为感觉很难读懂。上班了之后维护老项目,emmmm,也是源码吧。相对公司老项目代码, redux 源码看起来是那么赏心悦目、眉清目秀, 花了两天时间过了一下。

说实话有没有收获不知道,更多的是积累一些看源码的经验。

另外吐槽一下 yarn3, link 半天不行(也许是我太菜了),最后换了 pnpm link 上了