Redux原理

298 阅读12分钟

笔记来源:拉勾教育 - 大前端就业集训营

文章内容:学习过程中的笔记、感悟、和经验

提示:项目实战类文章资源无法上传,仅供参考

Redux原理

核心逻辑 / API - cerateStore()

形参:

  • reducer函数
  • preloadedState:预存储的store状态,即原始状态
  • enhancer(增加store功能),后面讨论

返回对象:

  • getState:用来获取状态
  • dispatch:触发指令相关操作
  • subscribe:订阅状态,可被多次调用

模拟cerateStore并使用

// 模拟 cerateStore 方法
function cerateStore(reducer, preloadedState) {
  // 获取初始状态
  let data = preloadedState
  // 订阅者
  let listeners = []
  // 获取状态方法
  function getState() {
    // 直接返回状态
    return data
  }
  // 指令触发方法
  function dispatch(action) {
    // 调用参数返回结果
    data = reducer(data, action)
    // 遍历订阅者触发订阅者
    for (const listen of listeners) {
      listen()
    }
  }
  // 添加订阅者方法
  function subscribe(cb) {
    // cb 为订阅者要触发的回调函数
    listeners.push(cb)
  }
  // 返回三个方法组成的对象
  return { getState, dispatch, subscribe }
}
<body>
  <!-- 结构 -->
  <button id="up">+1</button>
  <span id="num">0</span>
  <button id="down">-1</button>
  
  <!-- 引入 cerateStore -->
  <script src="./myRedux.js"></script>
  <script>
    // 创建 cerateStore 参数 - reducer
    function reducer(state, action) {
      // 根据指令返回状态
      switch (action.type) {
        case 'up':
          return state + 1
        case 'down':
          return state - 1
        default:
          return state
      }
    }

    // 创建 store
    const store = cerateStore(reducer, 0)

    // 添加订阅者
    store.subscribe(() => {
      // 一旦触发,修改 inner 为 store 的状态
      document.getElementById('num').innerHTML = store.getState()
    })

    // 给两个按钮添加点击事件,调用 store.dispatch
    document.getElementById('up').onclick = function () {
      store.dispatch({ type: 'up' })
    }
    document.getElementById('down').onclick = function () {
      store.dispatch({ type: 'down' })
    }
  </script>
</body>

参数类型制约 - 完善cerateStore

使用throw new Error()进行报错处理

  • 限制参数reducer必须是一个函数 - Typof
  • 调用dispatch的时候传递的参数必须是个对象,并且必须有type属性
// 模拟 cerateStore 方法
function cerateStore(reducer, preloadedState) {
  // 直接判断 reducer 是否为函数
  if (typeof reducer !== 'function') throw new Error('reducer必须是一个对象')
  // 获取初始状态
  let data = preloadedState
  // 订阅者
  let listeners = []
  // 获取状态方法
  function getState() {
    // 直接返回状态
    return data
  }
  // 指令触发方法
  function dispatch(action) {
    // 调用函数判断是否为对象
    if (!isPlainObject(action)) throw new Error('参数必须是一个对象')
    // 判断是否含有type属性
    if (typeof action.type === 'undefined') throw new Error('需要传入type属性')
    // 调用参数返回结果
    data = reducer(data, action)
    // 遍历订阅者触发订阅者
    for (const listen of listeners) {
      listen()
    }
  }
  // 添加订阅者方法
  function subscribe(cb) {
    // cb 为订阅者要触发的回调函数
    listeners.push(cb)
  }
  // 返回三个方法组成的对象
  return { getState, dispatch, subscribe }
}

// 判断是否为非空对象
function isPlainObject(obj) {
  // 排除出数组外其他情况
  if (typeof obj !== 'object' || obj === null) return false
  // 根据顶层原型对象和自己原型对象判断
  let proto = obj
  while (Object.getPrototypeOf(proto) != null) {
    proto = Object.getPrototypeOf(proto)
  }
  return proto === Object.getPrototypeOf(obj)
}

实现Enhancer - 完善cerateStore

给cerate函数添加第三个形参(可选参数),对返回的store进行功能增强

  • 必须是个函数,使用之前先进行判断
  • Enhancer如果符合条件,那么会使用Enhancer创建一个强大的store
  • 不符合条件继续按照原始方法创建普通store
// 模拟 cerateStore 方法
function cerateStore(reducer, preloadedState, enhancer) {
  // 直接判断 reducer 是否为函数
  if (typeof reducer !== 'function') throw new Error('reducer必须是一个对象')
  // 判断是否传入了 enhancer
  if (typeof enhancer !== 'undefined') {
    // 传入了 enhancer 判断是否为函数
    if (typeof enhancer !== 'function')
      throw new Error('enhancer必须是一个函数')
    // 为函数调用函数并将 cerateStore 作为参数传递进去
    // 函数会返回一个函数,返回的函数立即被调用
    // 把 reducer 和 preloadedState 作为参数传入新函数
    return enhancer(cerateStore)(reducer, preloadedState)
  }
  // 获取初始状态
  let data = preloadedState
  // 订阅者
  let listeners = []
  // 获取状态方法
  function getState() {
    // 直接返回状态
    return data
  }
  // 指令触发方法
  function dispatch(action) {
    // 调用函数判断是否为对象
    if (!isPlainObject(action)) throw new Error('参数必须是一个对象')
    // 判断是否含有type属性
    if (typeof action.type === 'undefined') throw new Error('需要传入type属性')
    // 调用参数返回结果
    data = reducer(data, action)
    // 遍历订阅者触发订阅者
    for (const listen of listeners) {
      listen()
    }
  }
  // 添加订阅者方法
  function subscribe(cb) {
    // cb 为订阅者要触发的回调函数
    listeners.push(cb)
  }
  // 返回三个方法组成的对象
  return { getState, dispatch, subscribe }
}

// 判断是否为非空对象
function isPlainObject(obj) {
  // 排除出数组外其他情况
  if (typeof obj !== 'object' || obj === null) return false
  // 根据顶层原型对象和自己原型对象判断
  let proto = obj
  while (Object.getPrototypeOf(proto) != null) {
    proto = Object.getPrototypeOf(proto)
  }
  return proto === Object.getPrototypeOf(obj)
}
<body>
  <!-- 结构 -->
  <button id="up">+1</button>
  <span id="num">0</span>
  <button id="down">-1</button>
  <!-- 引入 cerateStore -->
  <script src="./myRedux.js"></script>
  <script>
    // 创建 cerateStore 参数 - reducer
    function reducer(state, action) {
      // 根据指令返回状态
      switch (action.type) {
        case 'up':
          return state + 1
        case 'down':
          return state - 1
        default:
          return state
      }
    }
    // 创建 cerateStore 参数 - enhancer
    function enhancer(cerateStore) {
      // 返回一个新函数
      return function (reducer, preloadedState) {
        // 调用 cerateStore 创建普通 store
        let store = cerateStore(reducer, preloadedState)
        // 下面自定义 dispatch
        const dispatch = store.dispatch
        // 创建新的 dispatch 用来替换旧的 dispatch,action 是调用时传递的参数
        function _dispatch_(action) {
          // 如果传递过来一个函数,执行函数,并且把原本的 dispatch 传递给函数
          if (typeof action === 'function') {
            return action(dispatch)
          }
          // 如果不是函数直接调用 dispatch 执行
          dispatch(action)
        }
        // 返回改造过的 store
        return { ...store, dispatch: _dispatch_ }
      }
    }
    // 创建 store
    const store = cerateStore(reducer, 0, enhancer)
    // 添加订阅者
    store.subscribe(() => {
      // 一旦触发,修改 inner 为 store 的状态
      document.getElementById('num').innerHTML = store.getState()
    })

    // 给两个按钮添加点击事件,调用 store.dispatch
    document.getElementById('up').onclick = function () {
      // 调用 dispatch 传递一个函数
      store.dispatch(dispatch => {
        // 函数内部逻辑
        setTimeout(() => {
          // 最后调用原本应该执行的 dispatch
          dispatch({ type: 'up' })
        }, 2000)
      })
    }
    document.getElementById('down').onclick = function () {
      // 不传入函数,直接传入需要执行的 dispatch
      store.dispatch({ type: 'down' })
    }
  </script>
</body>

梳理

  • cerateStore的第三个参数会创建一个比普通store更强大的store
  • enhancer使用前需要判断是否符合要求
  • 符合要求则直接调用,并且将原本的cerateStore传递进去
  • enhancer返回一个新函数,新函数立即调用,新函数接受cerateStore的前两个值,到此为止,cerateStore、reducer,、preloadedState都传递给enhancer了
  • 新函数通过cerateStore创建普通store,可以直接使用
  • 如果想在普通store上进行dispatch处理,则需要创建一个新的dispach替换掉旧的dispatch
  • 因为新的dispach没有旧的dispach逻辑,所以当需要使用逻辑的时候还是需要调用旧的dispach

中间件就是这样实现的

中间件实现原理 - applyMiddleware

中间件在action发出后、reducer接收前进行拦截处理

中间件的核心就是增强dispatch方法,当有多个中间件的时候,多个中间件依次执行,最后才触发action

// 中间件1
// 参数store:阉割版store,只包括 getState 和 dispatch
// 参数next:下一个中间件,如果当前是最后一个中间件,则 next 就是 dispatch
// 参数action:就是触发的 action
function one(store) {
  return function (next) {
    return function (action) {
      console.log('one中间件执行了')
      next(action)
    }
  }
}
// 中间件2
// 参数store:阉割版store,只包括 getState 和 dispatch
// 参数next:下一个中间件,如果当前是最后一个中间件,则 next 就是 dispatch
// 参数action:就是触发的 action
function tow(store) {
  return function (next) {
    return function (action) {
      console.log('tow中间件执行了')
      next(action)
    }
  }
}
这里创建store直接使用中间件函数导入中间件

<body>
  <!-- 结构 -->
  <button id="up">+1</button>
  <span id="num">0</span>
  <button id="down">-1</button>
  <!-- 引入 cerateStore -->
  <script src="./myRedux.js"></script>
  <!-- 引入中间件 -->
  <script src="./middleware/one.js"></script>
  <script src="./middleware/tow.js"></script>
  <script>
    // 创建 cerateStore 参数 - reducer
    function reducer(state, action) {
      // 根据指令返回状态
      switch (action.type) {
        case 'up':
          return state + 1
        case 'down':
          return state - 1
        default:
          return state
      }
    }
    // 创建 cerateStore 参数 - enhancer
    function enhancer(cerateStore) {
      // 返回一个新函数
      return function (reducer, preloadedState) {
        // 调用 cerateStore 创建普通 store
        let store = cerateStore(reducer, preloadedState)
        // 下面自定义 dispatch
        const dispatch = store.dispatch
        // 创建新的 dispatch 用来替换旧的 dispatch,action 是调用时传递的参数
        function _dispatch_(action) {
          // 如果传递过来一个函数,执行函数,并且把原本的 dispatch 传递给函数
          if (typeof action === 'function') {
            return action(dispatch)
          }
          // 如果不是函数直接调用 dispatch 执行
          dispatch(action)
        }
        // 返回改造过的 store
        return { ...store, dispatch: _dispatch_ }
      }
    }
    // 创建 store,这里使用中间件函数引入两个中间件
    const store = cerateStore(reducer, 0, applyMiddleware(one, tow))
    // 添加订阅者
    store.subscribe(() => {
      // 一旦触发,修改 inner 为 store 的状态
      document.getElementById('num').innerHTML = store.getState()
    })

    // 给两个按钮添加点击事件,调用 store.dispatch
    document.getElementById('up').onclick = function () {
      store.dispatch({ type: 'up' })
    }
    document.getElementById('down').onclick = function () {
      // 不传入函数,直接传入需要执行的 dispatch
      store.dispatch({ type: 'down' })
    }
  </script>
</body>
// 模拟 cerateStore 方法
function cerateStore(reducer, preloadedState, enhancer) {
  // 直接判断 reducer 是否为函数
  if (typeof reducer !== 'function') throw new Error('reducer必须是一个对象')
  // 判断是否传入了 enhancer
  if (typeof enhancer !== 'undefined') {
    // 传入了 enhancer 判断是否为函数
    if (typeof enhancer !== 'function')
      throw new Error('enhancer必须是一个函数')
    // 为函数调用函数并将 cerateStore 作为参数传递进去
    // 函数会返回一个函数,返回的函数立即被调用
    // 把 reducer 和 preloadedState 作为参数传入新函数
    return enhancer(cerateStore)(reducer, preloadedState)
  }
  // 获取初始状态
  let data = preloadedState
  // 订阅者
  let listeners = []
  // 获取状态方法
  function getState() {
    // 直接返回状态
    return data
  }
  // 指令触发方法
  function dispatch(action) {
    // 调用函数判断是否为对象
    if (!isPlainObject(action)) throw new Error('参数必须是一个对象')
    // 判断是否含有type属性
    if (typeof action.type === 'undefined') throw new Error('需要传入type属性')
    // 调用参数返回结果
    data = reducer(data, action)
    // 遍历订阅者触发订阅者
    for (const listen of listeners) {
      listen()
    }
  }
  // 添加订阅者方法
  function subscribe(cb) {
    // cb 为订阅者要触发的回调函数
    listeners.push(cb)
  }
  // 返回三个方法组成的对象
  return { getState, dispatch, subscribe }
}

// 判断是否为非空对象
function isPlainObject(obj) {
  // 排除出数组外其他情况
  if (typeof obj !== 'object' || obj === null) return false
  // 根据顶层原型对象和自己原型对象判断
  let proto = obj
  while (Object.getPrototypeOf(proto) != null) {
    proto = Object.getPrototypeOf(proto)
  }
  return proto === Object.getPrototypeOf(obj)
}

// 中间件方法,使用 ...将多个参数合并为一个数组
function applyMiddleware(...wares) {
  // 和 Enhancer 相同,上来依旧需要创建基本的 store
  return function (cerateStore) {
    return function (reducer, preloadedState) {
      // 创建 store - 基本
      let store = cerateStore(reducer, preloadedState)
      // 创建阉割版 store 用于带入中间件
      let middlewareAPI = {
        getState: store.getState,
        dispatch: store.dispatch,
      }
      // 执行所有中间件最外层函数,返回数组,该数组包含的事中间件外层返回的函数
      const chain = wares.map(ware => ware(middlewareAPI))
      // 遍历上面数组,执行中间件的第二层函数(解构),传递dispatch,最终返回
      const dispatch = compose(...chain)(store.dispatch)
      // 返回store,并替换dispatch
      return {
        ...store,
        dispatch,
      }
    }
  }
}

// 中间件第二层函数执行
function compose() {
  // 接受参数并由类数组转换为数组
  const funcs = [...arguments]
  // 返回函数,因为要使用 store.dispatch,这里的dispatch就是中间件里面的next参数
  return function (dispatch) {
    // 倒过来执行函数,因为前一个中间件需要使用后一个中间件返回的第三层函数
    for (let i = funcs.length - 1; i >= 0; i--) {
      // 修改参数为后面中间件的第三层函数
      dispatch = funcs[i](dispatch)
    }
    // 直接返回第一个中间件第二层函数
    return dispatch
  }
}

bindActionCreators

bindActionCreators方法本质上就是将原本需要手动调用dispatch的方式进行了封装,只需要调用bindActionCreators函数的对应方法名即可

// 模拟 cerateStore 方法
function cerateStore(reducer, preloadedState, enhancer) {
  // 直接判断 reducer 是否为函数
  if (typeof reducer !== 'function') throw new Error('reducer必须是一个对象')
  // 判断是否传入了 enhancer
  if (typeof enhancer !== 'undefined') {
    // 传入了 enhancer 判断是否为函数
    if (typeof enhancer !== 'function')
      throw new Error('enhancer必须是一个函数')
    // 为函数调用函数并将 cerateStore 作为参数传递进去
    // 函数会返回一个函数,返回的函数立即被调用
    // 把 reducer 和 preloadedState 作为参数传入新函数
    return enhancer(cerateStore)(reducer, preloadedState)
  }
  // 获取初始状态
  let data = preloadedState
  // 订阅者
  let listeners = []
  // 获取状态方法
  function getState() {
    // 直接返回状态
    return data
  }
  // 指令触发方法
  function dispatch(action) {
    // 调用函数判断是否为对象
    if (!isPlainObject(action)) throw new Error('参数必须是一个对象')
    // 判断是否含有type属性
    if (typeof action.type === 'undefined') throw new Error('需要传入type属性')
    // 调用参数返回结果
    data = reducer(data, action)
    // 遍历订阅者触发订阅者
    for (const listen of listeners) {
      listen()
    }
  }
  // 添加订阅者方法
  function subscribe(cb) {
    // cb 为订阅者要触发的回调函数
    listeners.push(cb)
  }
  // 返回三个方法组成的对象
  return { getState, dispatch, subscribe }
}

// 判断是否为非空对象
function isPlainObject(obj) {
  // 排除出数组外其他情况
  if (typeof obj !== 'object' || obj === null) return false
  // 根据顶层原型对象和自己原型对象判断
  let proto = obj
  while (Object.getPrototypeOf(proto) != null) {
    proto = Object.getPrototypeOf(proto)
  }
  return proto === Object.getPrototypeOf(obj)
}

// 中间件方法,使用 ...将多个参数合并为一个数组
function applyMiddleware(...wares) {
  // 和 Enhancer 相同,上来依旧需要创建基本的 store
  return function (cerateStore) {
    return function (reducer, preloadedState) {
      // 创建 store - 基本
      let store = cerateStore(reducer, preloadedState)
      // 创建阉割版 store 用于带入中间件
      let middlewareAPI = {
        getState: store.getState,
        dispatch: store.dispatch,
      }
      // 执行所有中间件最外层函数,返回数组,该数组包含的事中间件外层返回的函数
      const chain = wares.map(ware => ware(middlewareAPI))
      // 遍历上面数组,执行中间件的第二层函数(解构),传递dispatch,最终返回
      const dispatch = compose(...chain)(store.dispatch)
      // 返回store,并替换dispatch
      return {
        ...store,
        dispatch,
      }
    }
  }
}

// 中间件第二层函数执行
function compose() {
  // 接受参数并由类数组转换为数组
  const funcs = [...arguments]
  // 返回函数,因为要使用 store.dispatch,这里的dispatch就是中间件里面的next参数
  return function (dispatch) {
    // 倒过来执行函数,因为前一个中间件需要使用后一个中间件返回的第三层函数
    for (let i = funcs.length - 1; i >= 0; i--) {
      // 修改参数为后面中间件的第三层函数
      dispatch = funcs[i](dispatch)
    }
    // 直接返回第一个中间件第二层函数
    return dispatch
  }
}

// bindActionCreators 处理 action
// 参数1: action合集
// 参数2: dispatch
function bindActionCreators(arr, diapatch) {
  // 创建一个空对象
  const actions = {}
  // 遍历 action
  for (let key in arr) {
    // 给对象添加属性,对应 action
    actions[key] = function () {
      // 注意这里使用action返回值
      diapatch(arr[key]())
    }
  }
  // 直接返回处理后的 action
  return actions
}
<body>
  <!-- 结构 -->
  <button id="up">+1</button>
  <span id="num">0</span>
  <button id="down">-1</button>
  <!-- 引入 cerateStore -->
  <script src="./myRedux.js"></script>
  <!-- 引入中间件 -->
  <script src="./middleware/one.js"></script>
  <script src="./middleware/tow.js"></script>
  <script>
    // 创建 cerateStore 参数 - reducer
    function reducer(state, action) {
      // 根据指令返回状态
      switch (action.type) {
        case 'up':
          return state + 1
        case 'down':
          return state - 1
        default:
          return state
      }
    }
    // 创建 cerateStore 参数 - enhancer
    function enhancer(cerateStore) {
      // 返回一个新函数
      return function (reducer, preloadedState) {
        // 调用 cerateStore 创建普通 store
        let store = cerateStore(reducer, preloadedState)
        // 下面自定义 dispatch
        const dispatch = store.dispatch
        // 创建新的 dispatch 用来替换旧的 dispatch,action 是调用时传递的参数
        function _dispatch_(action) {
          // 如果传递过来一个函数,执行函数,并且把原本的 dispatch 传递给函数
          if (typeof action === 'function') {
            return action(dispatch)
          }
          // 如果不是函数直接调用 dispatch 执行
          dispatch(action)
        }
        // 返回改造过的 store
        return { ...store, dispatch: _dispatch_ }
      }
    }
    // 创建 store,这里使用中间件函数引入两个中间件
    const store = cerateStore(reducer, 0, applyMiddleware(one, tow))
    // 添加订阅者
    store.subscribe(() => {
      // 一旦触发,修改 inner 为 store 的状态
      document.getElementById('num').innerHTML = store.getState()
    })
    // 使用 bindActionCreators 处理 action
    const actions = bindActionCreators({ up, down }, store.dispatch)
    // 两个函数 直接返回 dispatch 要触发的 action 信息
    function up() {
      return { type: 'up' }
    }
    function down() {
      return { type: 'down' }
    }
    // 给两个按钮添加点击事件,调用 store.dispatch
    document.getElementById('up').onclick = function () {
      // store.dispatch({ type: 'up' })
      // 直接调用bindActionCreators处理后的方法名即可
      actions.up()
    }
    document.getElementById('down').onclick = function () {
      // store.dispatch({ type: 'down' })
      // 直接调用bindActionCreators处理后的方法名即可
      actions.down()
    }
  </script>
</body>

combineReducers - 合并reducer

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta http-equiv="X-UA-Compatible" content="IE=edge" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>Document</title>
  </head>
  <body>
    <!-- 结构 -->
    <button id="up">+1</button>
    <span id="num">0</span>
    <button id="down">-1</button>
    <!-- 引入 cerateStore -->
    <script src="./myRedux.js"></script>
    <!-- 引入中间件 -->
    <script src="./middleware/one.js"></script>
    <script src="./middleware/tow.js"></script>
    <script>
      // 创建 cerateStore 参数 - reducer
      function reducer(state, action) {
        // 根据指令返回状态
        switch (action.type) {
          case 'up':
            return state + 1
          case 'down':
            return state - 1
          default:
            return state
        }
      }
      // 创建 cerateStore 参数 - enhancer
      function enhancer(cerateStore) {
        // 返回一个新函数
        return function (reducer, preloadedState) {
          // 调用 cerateStore 创建普通 store
          let store = cerateStore(reducer, preloadedState)
          // 下面自定义 dispatch
          const dispatch = store.dispatch
          // 创建新的 dispatch 用来替换旧的 dispatch,action 是调用时传递的参数
          function _dispatch_(action) {
            // 如果传递过来一个函数,执行函数,并且把原本的 dispatch 传递给函数
            if (typeof action === 'function') {
              return action(dispatch)
            }
            // 如果不是函数直接调用 dispatch 执行
            dispatch(action)
          }
          // 返回改造过的 store
          return { ...store, dispatch: _dispatch_ }
        }
      }
      // 使用combineReducers合并 reducer
      const rootReducer = combineReducers({ num: reducer })
      // 创建 store,这里使用中间件函数引入两个中间件
      const store = cerateStore(
        // 使用合并后的reducer
        rootReducer,
        { num: 0 },
        applyMiddleware(one, tow)
      )
      // 添加订阅者
      store.subscribe(() => {
        // 一旦触发,修改 inner 为 store 的状态,因为reducer合并了,需要调用属性
        document.getElementById('num').innerHTML = store.getState().num
      })
      // 使用 bindActionCreators 处理 action
      const actions = bindActionCreators({ up, down }, store.dispatch)
      // 两个函数 直接返回 dispatch 要触发的 action 信息
      function up() {
        return { type: 'up' }
      }
      function down() {
        return { type: 'down' }
      }
      // 给两个按钮添加点击事件,调用 store.dispatch
      document.getElementById('up').onclick = function () {
        // store.dispatch({ type: 'up' })
        // 直接调用bindActionCreators处理后的方法名即可
        actions.up()
      }
      document.getElementById('down').onclick = function () {
        // store.dispatch({ type: 'down' })
        // 直接调用bindActionCreators处理后的方法名即可
        actions.down()
      }
    </script>
  </body>
</html>
// 模拟 cerateStore 方法
function cerateStore(reducer, preloadedState, enhancer) {
  // 直接判断 reducer 是否为函数
  if (typeof reducer !== 'function') throw new Error('reducer必须是一个对象')
  // 判断是否传入了 enhancer
  if (typeof enhancer !== 'undefined') {
    // 传入了 enhancer 判断是否为函数
    if (typeof enhancer !== 'function')
      throw new Error('enhancer必须是一个函数')
    // 为函数调用函数并将 cerateStore 作为参数传递进去
    // 函数会返回一个函数,返回的函数立即被调用
    // 把 reducer 和 preloadedState 作为参数传入新函数
    return enhancer(cerateStore)(reducer, preloadedState)
  }
  // 获取初始状态
  let data = preloadedState
  // 订阅者
  let listeners = []
  // 获取状态方法
  function getState() {
    // 直接返回状态
    return data
  }
  // 指令触发方法
  function dispatch(action) {
    // 调用函数判断是否为对象
    if (!isPlainObject(action)) throw new Error('参数必须是一个对象')
    // 判断是否含有type属性
    if (typeof action.type === 'undefined') throw new Error('需要传入type属性')
    // 调用参数返回结果
    data = reducer(data, action)
    // 遍历订阅者触发订阅者
    for (const listen of listeners) {
      listen()
    }
  }
  // 添加订阅者方法
  function subscribe(cb) {
    // cb 为订阅者要触发的回调函数
    listeners.push(cb)
  }
  // 返回三个方法组成的对象
  return { getState, dispatch, subscribe }
}

// 判断是否为非空对象
function isPlainObject(obj) {
  // 排除出数组外其他情况
  if (typeof obj !== 'object' || obj === null) return false
  // 根据顶层原型对象和自己原型对象判断
  let proto = obj
  while (Object.getPrototypeOf(proto) != null) {
    proto = Object.getPrototypeOf(proto)
  }
  return proto === Object.getPrototypeOf(obj)
}

// 中间件方法,使用 ...将多个参数合并为一个数组
function applyMiddleware(...wares) {
  // 和 Enhancer 相同,上来依旧需要创建基本的 store
  return function (cerateStore) {
    return function (reducer, preloadedState) {
      // 创建 store - 基本
      let store = cerateStore(reducer, preloadedState)
      // 创建阉割版 store 用于带入中间件
      let middlewareAPI = {
        getState: store.getState,
        dispatch: store.dispatch,
      }
      // 执行所有中间件最外层函数,返回数组,该数组包含的事中间件外层返回的函数
      const chain = wares.map(ware => ware(middlewareAPI))
      // 遍历上面数组,执行中间件的第二层函数(解构),传递dispatch,最终返回
      const dispatch = compose(...chain)(store.dispatch)
      // 返回store,并替换dispatch
      return {
        ...store,
        dispatch,
      }
    }
  }
}

// 中间件第二层函数执行
function compose() {
  // 接受参数并由类数组转换为数组
  const funcs = [...arguments]
  // 返回函数,因为要使用 store.dispatch,这里的dispatch就是中间件里面的next参数
  return function (dispatch) {
    // 倒过来执行函数,因为前一个中间件需要使用后一个中间件返回的第三层函数
    for (let i = funcs.length - 1; i >= 0; i--) {
      // 修改参数为后面中间件的第三层函数
      dispatch = funcs[i](dispatch)
    }
    // 直接返回第一个中间件第二层函数
    return dispatch
  }
}

// bindActionCreators 处理 action
// 参数1: action合集
// 参数2: dispatch
function bindActionCreators(arr, diapatch) {
  // 创建一个空对象
  const actions = {}
  // 遍历 action
  for (let key in arr) {
    // 给对象添加属性,对应 action
    actions[key] = function () {
      // 注意这里使用action返回值
      diapatch(arr[key]())
    }
  }
  // 直接返回处理后的 action
  return actions
}

// 合并reducer方法
function combineReducers(obj) {
  // 获取全部属性名
  const keys = Object.keys(obj)
  // 判断导入的reducer是不是都是函数,否则报错
  for (let i = 0; i < keys.length; i++) {
    if (typeof obj[keys[i]] !== 'function')
      throw new Error('reducer必须是一个函数')
  }
  // 返回一个函数,因为store创建时的reducer也必须是一个函数
  return function (state, action) {
    // 创建一个新的state
    const newState = {}
    // 遍历全部传入的 reducer,执行并传递给新的state中
    for (let i = 0; i < keys.length; i++) {
      newState[keys[i]] = obj[keys[i]](state[keys[i]], action)
    }
    // 直接导出新的state作为reducer返回值
    return newState
  }
}