Koa2和Redux中间件源码研究

一、Koa2中间件源码分析

在Koa2中,中间件被存放在一个数组中。 使用koa中,最常见的就是app.use(fn),use函数部分源码如下所示。首先中间件必须是个函数。若是generator函数,则需要进行转化。最后把该中间件推入middelaware数组中。

constructor() {this.middleware = [];
}
use(fn) {this.middleware.push(fn);
}

当调用app.listen函数时,实际上是创建了一个原生http服务器,并执行了Koa自身的callback方法。

listen(...args) {const server = http.createServer(this.callback());return server.listen(...args);
}

callback函数中compose是将中间件封装成一个迭代器,按照middleware数组中顺序执行下去,实现了面向切面编程。handleRequest函数则是将req和res封装成ctx,并执行中间件。

callback() {const fn = compose(this.middleware);const handleRequest = (req, res) => {const ctx = this.createContext(req, res);return this.handleRequest(ctx, fn);};return handleRequest;
}
handleRequest(ctx, fnMiddleware) {...return fnMiddleware(ctx).then(handleResponse).catch(onerror);
}

compose用来返回一个迭代器函数fnMiddleware,该函数接受两个参数,context和next。因为调用时只向fnMiddleware传入了第一个参数context,所以next参数的值一直是undefined。

首先定义了index,dispatch传入的参数 i 作为每个中间件的调用标志,每次中间件调用next函数都会让index 1,若是中间件多次调用next,则会使index大于等于该标志,就会报错。

然后依次读取中间件数组中的中间件,并将context和封装了调用标志i的dispatch函数传给中间件,就实现了中间件的过程。
当读取完中间件数组后,即i === middleware.length时,将值为是undefined的next传给fn并结束迭代。dispatch函数所有的返回值都是Promise函数,这样就可以实现异步编程。

function compose (middleware) {return function (context, next) {// last called middleware #let index = -1return dispatch(0)function dispatch (i) {if (i <= index) return Promise.reject(new Error('next() called multiple times'))index = ilet fn = middleware[i]if (i === middleware.length) fn = nextif (!fn) return Promise.resolve()try {return Promise.resolve(fn(context, dispatch.bind(null, i   1)));} catch (err) {return Promise.reject(err)}}}
}

二、Redux中间件源码分析

在Redux中需要引入中间件的话,需引入applyMiddleware函数并将其作为参数传给createStore。下方enhancer就是就是applyMiddleware的返回值。

export default function createStore(reducer, preloadedState, enhancer) {...return enhancer(createStore)(reducer, preloadedState)
}

applyMiddleware接受一个中间件数组为参数。

export default function applyMiddleware(...middlewares) {return createStore => (...args) => {const store = createStore(...args)let dispatch = () => {throw new Error(`Dispatching while constructing your middleware is not allowed. `  `Other middleware would not be applied to this dispatch.`)}const middlewareAPI = {getState: store.getState,dispatch: (...args) => dispatch(...args)}const chain = middlewares.map(middleware => middleware(middlewareAPI))dispatch = compose(...chain)(store.dispatch)return {...store,dispatch}}
}

其中compose用Array.prototype.reduce将中间件数组封装成一个层层包裹的函数。

function compose(...funcs) {return funcs.reduce((a, b) => (...args) => a(b(...args)))
}

若本来的中间件数组是

[a, b, c]

经过compose函数封装后,就成了

(...arg) => a(b(c(...arg)))

    dispatch = compose(...chain)(store.dispatch)

经过这个过程后,每次调用dispatch就会在中间件中进行遍历。每个中间件都需要调用next(action)来保证中间件链都能被执行。

Redux中中间件的写法一般为

    const middleware = store => next => action => {...}

根据上面的分析,store参数就是middlewareAPI,但是其中的dispatch并不是真正的dispatch,这是为了防止在中间件中调用store.dispatch而导致重新遍历整个中间件链。next是下一个中间件,需要传递action,直到最后一个中间件时,next即是原始的store.dispatch。

三、Koa2和Redux中间件比较

在两者中都出现了compose函数。
Koa2中的compose函数实现原理是用dispatch函数自身迭代,是从左向右执行中间件函数。而Redux中的compose实现原理是用了数组的reduce方法,从右向左执行中间件函数。两者执行顺序虽然不同,一样的是先执行的中间件可以获取后执行的中间件的状态(store的状态或者context的状态),实现了面向切面编程。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/366982.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Web应用程序的简单插件系统

我们需要制作多个具有很多共享功能的基于Web的项目。 为此&#xff0c;某种插件系统将是一个不错的选择&#xff08;作为复制粘贴内容的替代方法&#xff09;。 有些框架&#xff08;例如grails&#xff09;可以选择制作Web插件&#xff0c;但大多数没有&#xff0c;因此需要实…

[转]C++ auto 关键字的使用

原文地址: https://www.cnblogs.com/KunLunSu/p/7861330.html C98 auto 早在C98标准中就存在了auto关键字&#xff0c;那时的auto用于声明变量为自动变量&#xff0c;自动变量意为拥有自动的生命期&#xff0c;这是多余的&#xff0c;因为就算不使用auto声明&#xff0c;变量依…

python模块之configparser

一 什么是configparser&#xff1f; configparser是用于解析配置文件的模块。什么是配置文件呢&#xff1f;包含配置程序信息的文件就称为配置文件。什么样的数据应该作为配置信息呢&#xff1f;需要修改但是不经常改的信息就可以作为配置信息&#xff0c;比如数据文件的路径。…

java的使用条件_Java使用条件语句和循环结构确定控制流

与任何程序设计语言一样&#xff0c;Java使用条件语句和循环结构确定控制流。本文将简单讲解条件、循环和switch。一、块作用域块(block)&#xff0c;即复合语句。是指由一对大括号括起来的若干条简单的Java语句。块确定了变量的作用域。比如&#xff1a;public class Code {st…

实现小程序canvas拖拽功能

组件地址 https://github.com/jasondu/wx-comp-canvas-drag 实现效果 如何实现 使用canvas使用movable-view标签 由于movable-view无法实现旋转&#xff0c;所以选择使用canvas 需要解决的问题 如何将多个元素渲染到canvas上如何知道手指在元素上、如果多个元素重叠如何知…

探索Apache Camel Core – Seda组件

Apache Camel中的seda组件与我在之前的博客中介绍的direct组件非常相似&#xff0c;但是以异步的方式。 为此&#xff0c;它使用java.util.concurrent.BlockingQueue作为默认实现来使消息排队并与主Route线程断开连接&#xff0c;然后在单独的线程中处理消息。 由于此BlockingQ…

Properties类和如何操作属性

Properties类继承关系java.lang.Object java.util.Dictionary<K,V> java.util.Hashtable<Object,Object> java.util.Properties所有已实现的接口&#xff1a; Serializable, Cloneable, Map<Object,Object> 直接已知子类&#xff1a; Provide…

Spring MVC:带有CNVR卷的REST应用程序。 2

在上一篇文章中&#xff0c;我快速概述了带有CNVR的Spring MVC REST项目的设置环境。 在这一部分中&#xff0c;我可以直接关注控制器和REST服务的演示。 通常&#xff0c;我将做一个简短的介绍&#xff0c;然后我将介绍控制器方法并解释所有关键时刻。 由于我将进一步讨论RES…

SCP 报错 not a regular file

在 scp 后 加 -r转载于:https://www.cnblogs.com/LYliangying/p/9815534.html

H5页面滚动阻尼效果实现

功能描述 要求 页面分为AB两个区域 当手机可视区的底部接触到 “阻尼带” 的时候&#xff0c;有个上拉弹性过程 当上拉到一定阈值程度就直接把B区顶部弹到手机可视区的顶部&#xff0c;让可视区从B区开始显示当上拉程度未到阈值&#xff0c;就回弹复原 当手机可视区从B区向上…

java面试题(杨晓峰)---第五讲String、StringBuffer、StringBuilder有什么区别?

线程 字符 操作频繁度 1 String &#xff08;1&#xff09;String的创建机制 由于String在java世界中使用过于频繁&#xff0c;java为了避免在一个系统中产生大量重复的String对象&#xff0c;引入了字符串常量池&#xff0c;其运行机制是&#xff1a;创建一个字符串时&am…

mysql怎么按年份分组_mysql - MYSQL按ID分组,但根据最近的年份进行拉取 - SO中文参考 - www.soinside.com...

我有一个包含以下内容的表&#xff1a;StudID Name Year SubjectID SubjectName MTFlag51280 ALOYSIUS 2019 42 CHINESE LANGUAGE 151280 ALOYSIUS 2020 70 ENGLISH LANGUAGE 051280 ALOYSIUS 2020 95 CHINESE B 151280 ALOYSIUS 2020 75 MATHEMATICS 051290 AMIL 2020 70 ENGL…

面向 Web 前端的原生语言总结手册

这一系列文章旨在让具有 Web 前端背景的开发者快速上手原生语言。 背景与动机 从 WebView 到 Hybrid 再到 React Native&#xff0c;移动端主流技术方案中前端同学的施展空间越来越大。但传统 Web 前端背景的同学所熟悉的编程语言主要是 JavaScript&#xff0c;在与 Native 协…

Java 8的新增功能(第二部分–可能会出现什么)

免责声明&#xff1a;我不为Oracle工作&#xff0c;也不以任何方式代表Oracle。 此功能列表不是官方的。 作为“局外人”&#xff0c;这只是我研究的一部分。 这是由三部分组成的系列文章的第二部分。 在第一部分中 &#xff0c;我谈到了Oracle正式让开发人员知道JavaFX 8中应…

MariaDB卸载

二进制安装方式的MariaDB卸载 关闭mysql服务service mysql stop 或 /etc/init.d/mysql stop 或 mysqladmin shutdown -uroot -p 删除数据文件和目录whereis mysql find / -name mysql rm -rf xxx 删除软链接&#xff0c;二进文件&#xff08;如有必要&#xff09;cd /usr/local…

aix系统java堆_浅谈AIX环境下的Java性能调优

1、什么是JavaJava 是一种面向对象的编程语言。它以 C 为模型&#xff0c;被设计成小的、简单的、在源和二进制级别跨平台的可移植的语言&#xff0c;Java 程序(applets 和应用程序)可以运行于任何已经安装了 Java 虚拟机(JVM)的机器上。Java 相对其它计算机语言有显著的优势&a…

web 前端 html

1&#xff0c;什么是web 在网络中&#xff0c;大量的数据需要有一个载体&#xff0c;而很多人都能够访问这个载体&#xff0c;利用浏览器的这个窗口链接一个有一个载体&#xff0c;这个载体就是网站也就是web的前身。  1&#xff0c;web标准&#xff1a;结构标准&#xff0c;表…

cf 1060e 树形dp 树上任意两点的距离和

题意&#xff1a; 给出一个树&#xff0c;把树上任意两个相隔一个点的点加一条边&#xff0c;问加完边之后任意两点的距离和是多少. 参考博客 &#xff1a;https://blog.csdn.net/Mr_Treeeee/article/details/82960566 思路&#xff1a;枚举边的贡献 算出所有点与点之间的距离&…

再谈前后端分离

前段时间我针对手头上的项目前端配置进行了反思以及总结并且写了两篇文章: webpack传统后端渲染的项目前端配置, webpack配置之前后端不分离, 很显然这些配置能满足一时的需求, 但是也有不足. 今天继续总结, 这里应该不涉及到具体后端语言, 只对前端配置进行描述. 毕竟配置工程…

JAVA中带有数字签名的XML安全性

介绍 如您所知&#xff0c;XML在我们的产品或项目开发中起着重要作用&#xff0c;并且从XML文档中我们收集了很多信息&#xff0c;而且我们可以对XML文件执行CRUD操作。 但是&#xff0c;关于如何确保XML文件中可用的数据是真实的以及数据来自受信任的可靠来源&#xff0c;这是…