手写redux系列(小白级教程,简单易懂)(二)-----实现redux的applyMiddleware

75 阅读5分钟

开启掘金成长之旅!这是我参与「掘金日新计划 · 2 月更文挑战」的第 9 天,点击查看活动详情

前言

手写redux系列(小白级教程,简单易懂)(一) - 掘金 (juejin.cn) 自从上次写了《手写redux系列》以后,好久没更新了,今天我们继续来实现redux中的applyMiddleware功能,来让他看上去更完善,本次的代码也是继续在上次的代码上进行编写,如果有小伙伴还没有看过redux系列文章一,也可以先看了后,把里面的代码实现之后,再来追更这篇文章

what is middleware?

和大多数框架一样,redux也不是完善的,所以他开了一个口子,可以供社区去开发一些中间件,来完善redux的功能。
例如我们常见的middleware: redux-thunk:用于异步操作、redux-logger:用于日志记录

我的理解是:middleware本身是一个函数,middleware是用来增强dispatch的,在dispatch分发action之前和之后增加一些功能,本身是一个装饰器模式

why do we learn applyMiddleware?

  • middleware 中间件,学会中间件,你就学会了代理模式
  • applyMiddleware 本身采用了装饰器模式,你又能学会装饰器
  • 如何把众多middleware 合并到一起,你又可以学习compose函数

这笔买卖发财了呀

理解enhancer函数概念

enhancer 的中文意思是增强器,是一个名词

那么在哪里会用到呢??

我们在上一次的文章中,实现了createStore函数,当时只接受了一个参数,reducer

function createStore(reducer){}

为了实现中间件功能,我们要增加enhancer作为第二个参数,也就是说第二个参数传入一个enhancer

function createStore(reducer,enhancer){}

enhancer的特点是什么?

enhancer是一个装饰器模式,装饰器模式的特点是什么,在不改变原有的结构和功能下,添加一些新的功能

装饰器模式一般怎么设计?

传入什么返回什么

redux的核心是什么?

createStore

所以我们把createStore传给enhancer,返回一个新的createStore

因为此处的enhancer主要是用来增强createStore的,所以我们也可以称为enhanceCreateStore

而增强功能的步骤在enhancer内部实现

现在我们在createStore内部先处理一下enhancer

function createStore(reducer, enhancer) {
  // 先处理enhancer
  // 如果enhancer存在并且是函数
  if (enhancer && typeof enhancer === 'function') {
    // 我们将createStore作为参数传给他
    // 传入什么返回什么,所以他会返回一个新的createStore给我们,所以名字叫newCreateStore
    const newCreateStore = enhancer(createStore);
    // 新的newCreateStore和原有的createStore函数一致,都是接受reducer
    // 得到的也一致,是一个store,但这是一个增强了功能的store,所以我们叫他newStore
    const newStore = newCreateStore(reducer);
    // 最后直接返回就行了
    return newStore;
  }

  // 闭包存储状态
  let state;
  // 初始化订阅者数组
  const listeners = [];
  ...
}

实现applyMiddleware

我们先在applyMiddleware中实现enhancer函数

function applyMiddleware(middleware) {
  // applyMiddleware 的返回值应该是一个enhancer
  // 按照我们前面说的enhancer的参数是createStore
  function enhancer(createStore) {
    // enhancer应该返回一个新的createStore
    function newCreateStore(reducer) {
      // 我们先写个空的newCreateStore, 直接返回createStore的结果
      const store = createStore(reducer);
    
      return store;
    }
    return newCreateStore;
  }
  return enhancer;
}

写好了上面的步骤以后,我们来实现中间件函数

中间件特点

  • 中间件也是返回一个增强器,是用来增强dispatch的,所以此处的middleware函数的返回值也可以叫做enhanceDispatch

  • 而且middleware的返回值enhanceDispatch也采用了装饰器模式,传入什么返回什么

在enhancer中处理中间件函数

function applyMiddleware(middleware) {
  // applyMiddleware 的返回值应该是一个enhancer
  // 按照我们前面说的enhancer的参数是createStore
  function enhancer(createStore) {
    // enhancer应该返回一个新的createStore
    function newCreateStore(reducer) {
      // 我们先写个空的newCreateStore, 直接返回createStore的结果
      const store = createStore(reducer);
      
      
      // middleware是一个函数,返回enhancerDispatch增强器
      const enhanceDispatch = middleware(store);
      // 获取老的dispatch
      const { dispatch } = store;
      // enhancerDispatch,是用来增强dispatch的,所以传入dispatch,返回一个新的dispatch
      const newDispatch = enhanceDispatch(dispatch)
      // 用新的dispatch,替换老的dispatch
      return { ...store, dispatch: newDispatch };
      
      
      
    }
    return newCreateStore;
  }
  return enhancer;
}

接下来我们手写一个logger中间件

  function logger(store) {
    // 传入dispatch返回一个新的dispatch
    function enhancerDispatch(dispatch) {
      // 新的dispatch和老的dispatch一样,接受action作为参数
      function newDispatch(action) {
        // 分发action前可以做一些处理
        let result = dispatch(action);
        // 分发action后可以做一些处理
        return result
      }
      return newDispatch;
    }
    // 返回enhanceDispatch增强器
    return enhanceDispatch;
  }

接下来添加一些打印日志功能

  function logger(store) {
    return function(next) {
      return function(action) {
        console.group(action.type);
        console.info('dispatching', action);
        let result = next(action);
        console.log('next state', store.getState());
        console.groupEnd();
        return result
      }
    }
  }

这样子我们的中间件功能就大功告成了

合并多个中间件

中间件都是什么类型?

都是函数类型,而且接受同样的参数

所以就是合并多个函数,并且执行,返回最终结果

让我们想到了compose函数,用来合并并执行函数的

compose函数实现

function compose(func) {
  // 如果只有一个函数的话
  if (func.length === 1) {
    // 返回第一个函数
    return func[0];
  }
  return func.reduce((a, b) => {
    // 此处返回什么,就证明compose是什么
    // 先执行a函数,在把a的结果传给b,在执行b
    return (...args) => b(a(...args));
  })
}

所以我们再改一下applyMiddleware函数

function applyMiddleware(...middlewares) {
  // applyMiddleware 的返回值应该是一个enhancer
  // 按照我们前面说的enhancer的参数是createStore
  function enhancer(createStore) {
    // enhancer应该返回一个新的createStore
    function newCreateStore(reducer) {
      // 我们先写个空的newCreateStore, 直接返回createStore的结果
      const store = createStore(reducer);
      // 因为我们只要是传入dispatch的,所以这边先展开middleware
      const chain = middlewares.map((middleware) => middleware(store));
      // 获取老的dispatch
      const { dispatch } = store;
      // 使用compose合并并执行中间件
      const enhanceDispatch = compose(chain);
      const newDispatch = enhanceDispatch(dispatch);
      // 用新的dispatch,替换老的dispatch
      return { ...store, dispatch: newDispatch };
    }
    return newCreateStore;
  }
  return enhancer;
}

接下来我们再写一个中间件用来测试

function logger2(store) {
    return function(next) {
      return function(action) {
        let result = next(action);
        console.log('logger2');
        return result
      }
    }
  }

image.png

最后点击一下我是周杰伦,我在油管新发布了一首歌,在控制台打印的信息包括了两个中间件的日志,所以我们的实现是没有问题的

完美,撒花 ✿✿ヽ(°▽°)ノ✿

参考