Анализ исходного кода Redux

Redux

предисловие

Я считаю, что многие студенты так же, как и я, заинтересованы в принципах и реализации более популярных интерфейсных библиотек, таких как (реакция, vue, webpack, redux, react-redux, react-router... и т. д.), но сначала прочитайте Когда исходный код неизбежно немного неуклюж, В этой статье интерпретация исходного кода Redux используется в качестве примера, чтобы дать вам некоторые идеи для интерпретации исходного кода.Эта статья объясняет для меня весь процесс интерпретации исходного кода Redux, и будут некоторые обходные пути, но Я все еще делюсь ею с вами, ведь эта статья пропагандирует процесс чтения.

Без лишних слов, давайте начнем играть в Redux напрямую.

нашел на гитхабеredux, клонировать на локальный

  目前最新版本: 4.0.1

Открываю проект, у меня привычка сначала глянуть на пакет, посмотреть соответствующие зависимости, есть ли peerDependencies и т.д.

Смотреть сейчас не на что, просто узнайте про упаковку с роллапом, а потом посмотрите на папку, Структура относительно обычная, смотрим прямо в src, да и файлов не так уж и много, как насчет этого? У вас есть уверенность?

Лично я считаю, что отладчик полезен при просмотре чужого кода: можно проверить порядок выполнения для повышения эффективности.

Ок, тогда редукс вынесем, гораздо удобнее уметь отлаживать.

Учащиеся, которые хотят программировать напрямую, пропускают этот шаг и см. позже.

Сначала используйте скаффолдинг, с которым вы более знакомы, чтобы быстро создать проект реакции (поскольку реакция-редукс будет проанализирована позже, поэтому используйте проект реакции напрямую), например, я выбираюcreate-react-app

  create-react-app redux-source-analyse

Переименуйте src в redux в redux и поместите его в src проекта redux-source-analyse, Далее, т.к. автор считает, что при чтении react-redux тоже есть потребность в таком проекте, прямо ставить официальный сайт reduxtodoListПроект впускают в проект, и директория проекта открывается вот так (на самом деле, исходникам редукса не нужно использовать этот тодолист)

Студенты, которые не хотят быть такими хлопотными, могут клонировать это напрямую.проектЗаметки внутри очень подробные, и рекомендуется читать эту статью в связке с этим проектом.

Мы официально начали исходный код

В соответствии со здравым смыслом мы, конечно же, решили сначала посмотреть файл index.js.

Эй, это очень просто, посмотрите на ссылку, внешнего пакета нет, а затем возвращается объект, конечно, этот объект является редуксом, на который мы ссылаемся,

isCrushed.name !=='isCrushed'用来判断是否压缩过,如果不是production环境且压缩了,给出warning

Далее рассмотрим ядро ​​createStore.js.

Давайте посмотрим, как он его использует, это функция, Параметры initState, applyMiddleware необязательны

 const store = createStore(rootReducer, initState, applyMiddleware(logger));

Затем, через некоторое время, это в основном то, что

Посмотрите прямо на код, и соответствующие пояснения прокомментированы.Автор смотрит на него сверху вниз, сначала посмотрите на часть спецификации параметров (... throw new Error('....')...) и затем посмотрите на функцию. Рекомендуется сначала посмотреть на API-интерфейсы, предоставляемые redux, такие как getState, subscribe, dispatch, и непосредственно посмотреть на связанные функции, даже на вспомогательные функции в utils (конечно, потому что вспомогательные функции redux относительно просты, если читать другие библиотеки, то сложно Можно место сначала пропустить, но нужно разобраться с функцией функции), чтение не гладкое, и нужны свои одноклассники-отладчикискачать его

Основные шаги чтения (... выбросить новую ошибку ('....') ...) -> пусть xxx -> getState -> подписаться -> диспетчеризация -> replaceReducer -> наблюдаемый

import ?observable from 'symbol-observable'

import ActionTypes from './utils/actionTypes'
import isPlainObject from './utils/isPlainObject'

// 先看这里, 就是我们调用的createStore function了
export default function createStore(reducer, preloadedState, enhancer) {
  // 如果 preloadedState和enhancer都为function,不支持,throw new Error
  // 我们都知道[initState]为object, [enhancer]为function

  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为function enhancer为undefined的时候说明initState没有初始化, 但是有middleware
  if (typeof preloadedState === 'function' && typeof enhancer === 'undefined') {
    enhancer = preloadedState // 把 preloadedState 赋值给 enhancer
    preloadedState = undefined // preloadedState赋值undeifined
  }

  // debugger
  // 如果参数enhancer存在
  if (typeof enhancer !== 'undefined') {
    // 如果enhancer存在,那他必须是个function, 否则throw Error哈
    if (typeof enhancer !== 'function') {
      throw new Error('Expected the enhancer to be a function.')
    }
    /**
     * 传入符合参数类型的参数,就可以执行 enhancer, 
     * 但是这个return深深的吸引了我, 因为说明有applyMiddleware的时候后面的都不用看了 ??? 当然不可能
     * 可是applyMiddleware其实是必用项,所以猜想一下applyMiddleware强化store之后会enhancer赋值undefined,再次调用createStore
     * 上下打个debugger看一下执行顺序(debugger位置以注释),果然不出所料
     * 好了, 假设我们还不知道applyMiddleware()这个funcrion具体干了什么,
     * 只知道他做了一些处理然后重新调用了createStore并且enhancer参数为undefined
     * 先记下,后续在看applyMiddleware, 因为我们现在要看的是createStore
     * * */
    // debugger
    return enhancer(createStore)(reducer, preloadedState)
  }
  // debugger

  // reducer要为function
  if (typeof reducer !== 'function') {
    throw new Error('Expected the reducer to be a function.')
  }

  // 简单过一下定义的变量
  let currentReducer = reducer  // 临时reducer
  let currentState = preloadedState // 临时init state
  let currentListeners = []  // 看名字,是个数组,起名Listeners,想到了什么? 我想到的是监听队列和观察者模式
  let nextListeners = currentListeners // 浅拷贝下这个队列
  let isDispatching = false // 我们很容易先假设isDispatching标志是否正在执行dispatch

  // 先看下各个函数的名字, 打眼一看getState,dispatch,subscribe都是比较熟悉的api
  // subscribe,observable再加上定义的数组,应该肯定是监听队列和观察者模式
  // 那我们先看看比较熟悉且暴露出来的api好了先看 -> getState

  // 其实这里是保存一份订阅快照
  function ensureCanMutateNextListeners() {
    //  不要忘了let nextListeners = currentListeners // 浅拷贝下这个队列
    // 判断nextListeners和当前的currentListeners是不是一个引用
    if (nextListeners === currentListeners) {
      // 如果是一个引用的话深拷贝出来一个currentListeners赋值给nextListener
      nextListeners = currentListeners.slice()
    }
  }

  // store.getState()获取当前的state
  function getState() {
    // dispatch中不可以getState, 为什么?
    // 因为dispatch是用来改变state的,为了确保state的正确性(获取最新的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.'
      )
    }
    // 确定currentState是当前的state 看 -> subscribe
    return currentState
  }

  // store.subscribe方法设置监听函数,一旦触发dispatch,就自动执行这个函数
  // listener是一个callback function
  function subscribe(listener) {
    // 类型判断
    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-reference/store#subscribe(listener) for more details.'
      )
    }

    // 不确定这个变量,猜测是订阅标记,先往下看
    let isSubscribed = true
    // ensureCanMutateNextListeners干啥的,点击去看一下
    ensureCanMutateNextListeners()
    // push一个function,明显的观察者模式,添加一个订阅函数
    nextListeners.push(listener)
    // 返回取消的function(unsubscribe)
    return function unsubscribe() {
      // 还记得let isSubscribed = true吧, 用来标记是否有listerner的
      if (!isSubscribed) {
        // 没有直接return
        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-reference/store#subscribe(listener) for more details.'
        )
      }

      // 这里解释了isSubscribed,
      isSubscribed = false

      // 保存订阅快照
      ensureCanMutateNextListeners()
      // 找到并删除当前的listener
      const index = nextListeners.indexOf(listener)
      nextListeners.splice(index, 1)
    }
  }

  // 发送一个action
  function dispatch(action) {
    // 看下util的isPlainObject
    // acticon必须是由Object构造的函数, 否则throw Error
    if (!isPlainObject(action)) {
      throw new Error(
        'Actions must be plain objects. ' +
          'Use custom middleware for async actions.'
      )
    }

    // 判断action, 不存在type throw Error
    if (typeof action.type === 'undefined') {
      throw new Error(
        'Actions may not have an undefined "type" property. ' +
          'Have you misspelled a constant?'
      )
    }

    // dispatch中不可以有进行的dispatch
    if (isDispatching) {
      throw new Error('Reducers may not dispatch actions.')
    }

    try {
      // 执行时标记为true
      isDispatching = true
      // 执行reducer, 来,回忆一下reducer,参数state, action 返回值newState
      // 这就是dispatch一个action可以改变全局state的原因
      currentState = currentReducer(currentState, action)
    } finally {
      // 最终执行, isDispatching标记为false, 即完成状态
      isDispatching = false
    }

    // 监听队列
    // 所有的的监听函数赋值给 listeners
    const listeners = (currentListeners = nextListeners)
    for (let i = 0; i < listeners.length; i++) {
      const listener = listeners[i]
      // 执行每一个监听函数
      listener()
    }
    // 返回传入的action
    return action
    // 到这里dispatch方法就结束了, 我们来思考总结一下, 为什么要用listeners
    // 当dispatch发送一个规范的action时,会更新state
    // 但是state改变了之后我们需要做一些事情, 比如更新ui既数据驱动视图
    // (当然一般我们使用react,react-redux的时候, 他们会帮我们完成这些事情)
    // 所以要提供一个监听模式,当然还要有一个监听函数subscribe, 保证dispatch和subscribe之间的一对多的模式
  }

  // 这是一个高级的api, 用于替换计算 state的reducer,不知道的同学面壁去
  // 哈哈开玩笑的确实很不常用, 官网也没怎么介绍
  // redux 热加载机制的时候用到了
  function replaceReducer(nextReducer) {
    // 既然是替换reducer, 类型要保持一直,不是function的滚远点
    if (typeof nextReducer !== 'function') {
      throw new Error('Expected the nextReducer to be a function.')
    }

    // 当前的currentReducer更新为参数nextReducer
    currentReducer = nextReducer
    // 和INIT的dispath相同,发送一个dispatch初始化state,表明一下是REPLACE
    // 自己👀看一下utils方法的ActionTypes, 随性的随机数
    dispatch({ type: ActionTypes.REPLACE })
  }

  // 不知道是干什么的, 先看看哪里用到了, 全局收索一下
  // 我TM!只有这一个地方有这个函数,而且没被使用( [?observable]: observable ), 就问你气不气?
  // 当然不气, 作为不思进取的我觉得不用看这部分了, 算了,简单的过一下, 刚好也不知道?observable这个私有属性的作用
  // 好了, 全局搜索一下?observable, 尼玛,对于我这种码农来说, 貌似又是没用的
  // 好吧,我们看一下作者的注释和代码
  function observable() {
    const outerSubscribe = subscribe
    // 
    return {
      /**
       * The minimal observable subscription method.
       * @param {Object} observer Any object that can be used as an observer.
       * The observer object should have a `next` method.
       * @returns {subscription} An object with an `unsubscribe` method that can
       * be used to unsubscribe the observable from the store, and prevent further
       * emission of values from the observable.
       */
      // 参数明显是object
      subscribe(observer) {
        if (typeof observer !== 'object' || observer === null) {
          throw new TypeError('Expected the observer to be an object.')
        }
        //获取观察着的状态
        function observeState() {
          // 如果有next方法,吧回调state
          if (observer.next) {
            observer.next(getState())
          }
        }

        observeState()
        //返回取消订阅的方法
        const unsubscribe = outerSubscribe(observeState)
        return { unsubscribe }
      },

      [?observable]() {
        return this // 猜测this应该是store
      }
      // observable方法简单过一下,不做过多解释,有了解的同学,欢迎不吝赐教
    }
  }

  // 有没有想过,在使用redux的时候, 初始化的state哪来的
  // 当然是自己先dispatch了一下
  //reducer 返回其初始状态 
  //初始化 store 里的 state tree
  dispatch({ type: ActionTypes.INIT })

  // 这个就是返回的store嘛
  return {
    dispatch,
    subscribe,
    getState,
    replaceReducer,
    [?observable]: observable
  }
}

После прочтения createStore у меня есть некоторое представление об общей реализации redux.Далее давайте посмотрим на CombineReducers

Наиболее привлекательной вещью для меня является функция экспорта по умолчанию combReducers, Другие функции, кажется, используются для выдачи некоторых предупреждений. Итак, сначала посмотрите на эту функцию combReducers (не смотрите на вышеприведенное 👀, перейдите непосредственно к combReducers )

import ActionTypes from './utils/actionTypes'
import warning from './utils/warning'
import isPlainObject from './utils/isPlainObject'

/**
 *  先过一下文件最吸引我的是export default function combineReducers
 *  先看这个combineReducers function
 * 
 */
function getUndefinedStateErrorMessage(key, action) {
  const actionType = action && action.type
  const actionDescription =
    (actionType && `action "${String(actionType)}"`) || 'an action'

  return (
    `Given ${actionDescription}, reducer "${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.`
  )
}

function getUnexpectedStateShapeWarningMessage(
  inputState,
  reducers,
  action,
  unexpectedKeyCache
) {
  const reducerKeys = Object.keys(reducers)
  const argumentName =
    action && action.type === ActionTypes.INIT
      ? 'preloadedState argument passed to createStore'
      : 'previous state received by the reducer'

  if (reducerKeys.length === 0) {
    return (
      'Store does not have a valid reducer. Make sure the argument passed ' +
      'to combineReducers is an object whose values are reducers.'
    )
  }

  if (!isPlainObject(inputState)) {
    return (
      `The ${argumentName} has unexpected type of "` +
      {}.toString.call(inputState).match(/\s([a-z|A-Z]+)/)[1] +
      `". Expected argument to be an object with the following ` +
      `keys: "${reducerKeys.join('", "')}"`
    )
  }

  const unexpectedKeys = Object.keys(inputState).filter(
    key => !reducers.hasOwnProperty(key) && !unexpectedKeyCache[key]
  )

  unexpectedKeys.forEach(key => {
    unexpectedKeyCache[key] = true
  })

  if (action && action.type === ActionTypes.REPLACE) return

  if (unexpectedKeys.length > 0) {
    return (
      `Unexpected ${unexpectedKeys.length > 1 ? 'keys' : 'key'} ` +
      `"${unexpectedKeys.join('", "')}" found in ${argumentName}. ` +
      `Expected to find one of the known reducer keys instead: ` +
      `"${reducerKeys.join('", "')}". Unexpected keys will be ignored.`
    )
  }
}

function assertReducerShape(reducers) {
  Object.keys(reducers).forEach(key => {
    const reducer = reducers[key]
   // reducer返回值
    const initialState = reducer(undefined, { type: ActionTypes.INIT })
    // undefined throw Error
    if (typeof initialState === 'undefined') {
      throw new Error(
        `Reducer "${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.`
      )
    }

    // 很明显assertReducerShape是用于reducer的规范
    // 回到combineReducers
    if (
      typeof reducer(undefined, {
        type: ActionTypes.PROBE_UNKNOWN_ACTION()
      }) === 'undefined'
    ) {
      throw new Error(
        `Reducer "${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.`
      )
    }
  })
}

// 用于合并reducer 一般是这样combineReducers({a,b,c})
export default function combineReducers(reducers) {
  // reducers中key的数组
  const reducerKeys = Object.keys(reducers)
  // 最终的reducer
  const finalReducers = {}
  for (let i = 0; i < reducerKeys.length; i++) {
    // 接受当前的key
    const key = reducerKeys[i]

    // 如果不是生产环境, 当前的reducer是undefined会给出warning
    if (process.env.NODE_ENV !== 'production') {
      if (typeof reducers[key] === 'undefined') {
        warning(`No reducer provided for key "${key}"`)
      }
    }

    // reducer要是一个function
    if (typeof reducers[key] === 'function') {
      // 赋值给finalReducers
      finalReducers[key] = reducers[key]
    }
    // 循环结束, 目的为了给finalReducers赋值, 过虑了不符合规范的reudcer
  }
  // 符合规范的reducer的key数组
  const finalReducerKeys = Object.keys(finalReducers)

  // 意想不到的key, 先往下看看
  let unexpectedKeyCache
  if (process.env.NODE_ENV !== 'production') {
    // production环境为{}
    unexpectedKeyCache = {}
  }

  let shapeAssertionError

  try {
    // 看这个function
    assertReducerShape(finalReducers)
  } catch (e) {
    shapeAssertionError = e
  }

  // 返回function, 即为createstore中的reducer参数既currentreducer
  // 自然有state和action两个参数, 可以回createstore文件看看currentReducer(currentState, action)
  return function combination(state = {}, action) {
    // reducer不规范报错
    if (shapeAssertionError) {
      throw shapeAssertionError
    }

    // 比较细致的❌信息,顺便看了一下getUndefinedStateErrorMessage,都是用于提示warning和error的, 不过多解释了
    if (process.env.NODE_ENV !== 'production') {
      const warningMessage = getUnexpectedStateShapeWarningMessage(
        state,
        finalReducers,
        action,
        unexpectedKeyCache
      )
      if (warningMessage) {
        warning(warningMessage)
      }
    }

    let hasChanged = false
    const nextState = {}
    for (let i = 0; i < finalReducerKeys.length; i++) {
      // 获取finalReducerKeys的key和value(function)
      const key = finalReducerKeys[i]
      const reducer = finalReducers[key]
      // 当前key的state值
      const previousStateForKey = state[key]
      // 执行reducer, 返回当前state
      const nextStateForKey = reducer(previousStateForKey, action)
      // 不存在返回值报错
      if (typeof nextStateForKey === 'undefined') {
        const errorMessage = getUndefinedStateErrorMessage(key, action)
        throw new Error(errorMessage)
      }
      // 新的state放在nextState对应的key里
      nextState[key] = nextStateForKey
      // 判断新的state是不是同一引用, 以检验reducer是不是纯函数
      hasChanged = hasChanged || nextStateForKey !== previousStateForKey
    }
    // 改变了返回nextState
    return hasChanged ? nextState : state
  }
  /*
  *  新版本的redux这部分改变了实现方法
  *  老版本的redux使用的reduce函数实现的
  *  简单例子如下
  * function combineReducers(reducers) {
  *    return (state = {}, action) => {
  *        return Object.keys(reducers).reduce((currentState, key) => {
  *            currentState[key] = reducers[key](state[key], action);
  *             return currentState;
  *         }, {})
  *      };
  *    }
  * 
  * */
}

На данный момент кажется, что вы в основном знакомы с ядром redux, но не волнуйтесь, есть важное applyMiddleware, ха-ха, сначала медленно посмотрите на bindActionCreator.js

Должны быть некоторые ученики, которые не знают этого API. Я сначала посмотрю на функцию BindatocaceCreators, и знакомые студенты могут напрямую увидеть реализацию.

/**
 *
 * @export
 * @param {*} actionCreators  一个 action creator,或者一个 value 是 action creator 的对象。
 * @param {*} dispatch 一个由 Store 实例提供的 dispatch 函数。
 * @returns 一个与原对象类似的对象,只不过这个对象的 value 都是会直接 dispatch 原 action creator 返回的结果的函数。
 *          如果传入一个单独的函数作为 actionCreators,那么返回的结果也是一个单独的函数。
 * 
 * 场景: 惟一会使用到 bindActionCreators 的场景是当你需要把 action creator 往下传到一个组件上,
 *        却不想让这个组件觉察到 Redux 的存在,而且不希望把 dispatch 或 Redux store 传给它。
 * /

Реализация bindActionCreator

function bindActionCreator(actionCreator, dispatch) {
  // 闭包
  return function() {
    // 执行后返回结果为传入的actionCreator直接调用arguments
    return dispatch(actionCreator.apply(this, arguments))
  }
}
// 先看这里 🔥🔥🔥
export default function bindActionCreators(actionCreators, dispatch) {
  // actionCreators为function
  if (typeof actionCreators === 'function') {
    return bindActionCreator(actionCreators, dispatch)
  }

  // 不是object throw Error
  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"?`
    )
  }

  // object 转为数组
  const keys = Object.keys(actionCreators)
  // 定义return 的props
  const boundActionCreators = {}
  for (let i = 0; i < keys.length; i++) {
    // actionCreators的key 通常为actionCreators function的name(方法名)
    const key = keys[i]
    // function => actionCreators工厂方法本身
    const actionCreator = actionCreators[key]
    if (typeof actionCreator === 'function') {
      // 参数为{actions:function xxx}是返回相同的类型
      boundActionCreators[key] = bindActionCreator(actionCreator, dispatch)
    }
  }
  // return 的props
  return boundActionCreators
}

Наконец, мы смотрим на applyMiddeware.js.

Подождите, сначала объясните compose, метод в функциональном программировании

 Composes functions from right to left.

Составление функций, объединение функций для выполнения справа налево Импорт композиций из './compose' здесь может быть перенесен из функционального программирования.

涨这样:
compose(funcA, funcB, funcC) === compose(funcA(funcB(funcC())))
 import compose from './compose'

/***
 * 
 * middleware既中间件,简单说在redux中作为扩展 dispatch 的唯一标准的方式。
 * 不熟悉的同学自行去api了解一下, 大致结构是这样的
 *  middleware = (store) => (next) =>(action) =>{ [return next(action)]}
 *  为了方便debugger我们先自己写一个简单的logger middleware,看->src/index.js
*/
// applyMiddleware用来添加中间件,在修改数据的时候redux通过改造dispatch来实现中间件.
// 来吧,揭开applyMiddleware的神秘面纱
export default function applyMiddleware(...middlewares) {
  // 返回一个名为createStore的function
  // 不知道你还是否记得createStore.js开头的这段代码
  /*
    if (typeof enhancer !== 'undefined') {
    if (typeof enhancer !== 'function') {
      throw new Error('Expected the enhancer to be a function.')
    }
    return enhancer(createStore)(reducer, preloadedState)
    }
    嗯哼?对上了吧, 有applyMiddleware的时候直接先执行这里, 没绕过来的同学debugger一下
  * */
  // 直接return createStore function
  // 这里我们看下执行顺序, 我们写一点伪代码,每一个变量是代码中debugger的位置
  /**
   *   createStore.js 
   *   d1 = createStore(reducer, initstate, enhancer){ ... debugger if (typeof enhancer !== 'undefined')}
   * 
   *   d2 =  if (typeof enhancer !== 'undefined') {
                      if (typeof enhancer !== 'function') {
                        throw new Error('Expected the enhancer to be a function.')
                      }
                      debugger
                    return enhancer(createStore)(reducer, preloadedState)
                    }
   *   d3 = if (typeof enhancer !== 'undefined') {} debugger
   *   
   *   d4 =  ... debugger const middlewareAPI = {
                      // copy getState
                      getState: store.getState,
                      dispatch: (...args) => dispatch(...args)
                    }
      
        d5 =    ... debugger  const store = createStore(...args)...
   *   
   *   执行顺序
   *   创建store的首先是调用createStore(...applyMiddleware()) 大致发生了这样的流程
   *   createStore(...applyMiddleware()) -> applyMiddleware() -> return function -> d1 -> d2 
   *   接下来
   *   return enhancer(createStore)(reducer, preloadedState) -> d5 -> createStore(...args)再次调用createStore -> d1
   *   接下来走d3下面的store初始化 -> dispatch(init) -> d4 -> 组合middleware,合并new dispatch -> 返回增强的store
   */
  return createStore => (...args) => {
    // 保存createStore(reducer, initstate) || createStore(reducer), 赋值给store
    // debugger
    const store = createStore(...args)
    // 定义了一个dispatch, 调用会 throw new Error(dispatching虽然构造middleware但不允许其他middleware应用 )
    let dispatch = () => {
      throw new Error(
        `Dispatching while constructing your middleware is not allowed. ` +
          `Other middleware would not be applied to this dispatch.`
      )
    }
    // debugger
    // 定义middlewareAPI, 中间件中的store  eg ->  logger(store)
    const middlewareAPI = {
      // add getState
      getState: store.getState,
      // 添加dispatch并包装一个function, 参数为(reducer, [initstate])
      // 向下看一看middlewareAPI作为参数被回调回去,不难理解, 告诉dispath不能再middleware插件中构造
      dispatch: (...args) => dispatch(...args)
    }
    // 调用每一个这样形式的middleware = store => next => action =>{}, 
    // 组成一个这样[f(next)=>acticon=>next(action)...]的array,赋值给chain
    const chain = middlewares.map(middleware => middleware(middlewareAPI))
    // debugger
    // compose看 -> compose.js文件
    // compose(...chain)会形成一个调用链, next指代下一个函数的注册, 这就是中间件的返回值要是next(action)的原因
    // 如果执行到了最后next就是原生的store.dispatch方法
    dispatch = compose(...chain)(store.dispatch) 
    // 返回增强的store
    return {
      ...store,
      dispatch
    }
  }
}

Наконец, давайте перейдем к compose.js, чтобы задать вопрос

 // 其实这个很有意思,是函数编程中的方法
//  我们来做一到题
//  实现这个样一个function -> compose(funcA, funcB, funcC) 形象为 compose(funcA(funcB(funcC()))))
//  返回值为一个(...args)=>(funcA(funcB(funcC(...args)))))
/**
 *
 * 你可能会这样写, 或者是for循环 
 * 
 *  function Compose(...funcs){
      if (funcs.length === 0) {
        return args => args;
      }
      if (funcs.length === 1) {
        return funcs[0]
      }
      const arr = funcs;
      let firstFun = arr[0];
      let len = arr.length;
      let i = 1;
      while(i !== len) {
        firstFun = firstFun(arr[i]);
        i++;
      }
      return firstFun;
    }
 * 
 * 
 */
// 好啦, 我们看看优秀的答案吧 👇

export default function compose(...funcs) {
  if (funcs.length === 0) {
    return arg => arg
  }

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

  // 是不是很巧妙
  // 其实compose是redux作者从函数式编程中移过来的, 有兴趣的同学去了解一下
  // 插个话, 因为compose的执行顺序原因, 所以有的middleware插件会要求要放在最后面
  return funcs.reduce((a, b) => (...args) => a(b(...args)))
}

Ну вот мы и прочитали исходный код redux

Автор считает, что исходный код редукса не сложный.Главная суть в его идее реализации.Давайте рассмотрим три принципа редукса.

1. В сторе хранится состояние всего приложения, а стор всего один.

2. Состояние в хранилище доступно только для чтения, и единственный способ изменить состояние — отправить действие.

3. Чистая функция (редьюсер) изменяет состояние, каждый раз возвращая новое состояние, и не может напрямую изменять исходный объект.

В следующий раз давайте проанализируем преимущества этого

И можем ли мы сами написать простой редукс

Это первый раз, чтобы написать блог, и это так утомительно😫, я настаиваю на обновлении и разделении в будущем, O (∩_∩) o Спасибо, господа, чтобы общаться, делиться и руководство.

прикреплятьgithubадрес