Redux 原理

311 阅读2分钟

一 目标

  • 实现 createStore 函数
  • 讲清楚中间件原理(本文重点)

二 实现 createStore 函数

用法 createStore(countReducer, applyMiddleware())

function createStore(reducer, enhancer) {
  if (enhancer) {
    return enhancer(createStore)(reducer)
  }

  let currentState
  let currentListener = []

  function getState() {
    return currentState
  }

  function dispatch(action) {
    currentState = reducer(currentState, action)
    currentListener.forEach(listener => listener())
    return action
  }

  function subscribe(listener) {
    currentListener.push(listener)
    return () => {
      const index = currentListener.indexOf(listener)
      currentListener.splice(index, 1)
    }
  }

  // 初始化数据
  dispatch({ type: ''})

  return {
    getState,
    dispatch,
    subscribe
  }
}
  • createStore函数的接收两个参数,第一个参数是reducer, 第二参数是 applyMiddleware()函数执行后的增强器,对 dispatch 增强
  • 执行 dispatch 会依次执行所有的中间件函数,最后执行createStore里的dispatch

三 中间件原理

中间件函数的格式

function middleware({ getState, dispatch }) {
  return function(next) {
    return function(action) {
      // todo stuff
      return next(action)
    }
  }
}
  • 中间件位于 store.dispatch 的顶层
  • 每个中间件都接收一个对象包含 getState, dispatch 属性
  • 传给下一个中间件之前做自己的事情,最终执行 createStore 的 dispatch

下面实现两个简单的中间件

function thunk({ getState, dispatch }) {
  return (next1) => (action) => {
    if (typeof action === 'function') {
      return action(dispatch, getState)
    }
    next1(action)
  }
}

function logger({ getState, dispatch }) {
  return (next2) => (action) => {
    const prevState = getState()
    console.log('prev state:', prevState)

    const value = next2(action)
    const nextState = getState()

    console.log('next state: ', nextState)
    return value
  }
}
  • thunk 就是dispatch中可以执行异步
  • logger 打印state的值

实现 applyMiddleware 函数

function applyMiddleware(...middlewares) {
  return (createStore) => (reducer) => {
    const store = createStore(reducer)
    let dispatch = store.dispatch
    const midApi = {
      getState: store.getState,
      dispatch: (action) => dispatch(action)
    }
    const chain = middlewares.map(middleware => middleware(midApi))
    dispatch = compose(...chain)(store.dispatch)
    return { ...store, dispatch}
  }
}

function compose(...funs) {
  if (funs.length === 0) {
    return (arg) => arg;
  }

  if (funs.length === 1) {
    return funs[0];
  }

  return funs.reduce((a, b) => (...args) => a(b(...args)));
}

上面说了 applyMiddleware 函数是增强 dispatch

调用中间件函数

applyMiddleware(logger, thunk)

传参的顺序会决定中间件的调用顺序,下面重点讲解一下中间件函数到底是怎么执行的,对 compose 函数不是太了解的,请先阅读另一个篇文章 函数组合

最难懂就是下面两行代码,把这两行代码搞懂了就明白了中间件到底是怎么调用的

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

chain 中保存就是每个中间件执行后返回的内层函数

alt 属性文本

由于compose函数是从右向左依次执行函数的,所以执行这行代码的时候 compose(...chain)(store.dispatch)

  • 先执行chain[1],参数next1就是store.dispatch
  • 再执行chain[0],参数next2就是chain[1]的返回值
  • 最后的返回值就是chain[0]中的 (action) = {}, 赋值给createStore中的dispatch,得到增强后的dispatch

调用store.dispatch时的执行顺行时和上面的执行顺序相反

  • 先调用logger 中的 (action)=> {} 函数
  • 再执行 thunk 中的 (action)=> {} 函数
  • 最后执行createStore里的dispatch

四 总结

  • 实现了 createStore() 函数
  • 熟悉 redux 中间件的写法
  • 分析了中间件函数的执行流程