搜索
首页web前端js教程比较一下nodejs中间件Koa和Express
比较一下nodejs中间件Koa和ExpressFeb 24, 2021 am 09:52 AM
expressnode

比较一下nodejs中间件Koa和Express

相关推荐:《nodejs 教程

说到中间件,很多开发者都会想到 Koa.js,其中间件设计无疑是前端中间件思想的典型代表之一。

最近重新温习这部分内容,按奈不住想要和各位看官聊聊其中绝妙!


Koa用起来非常方便——比之express,它“完美中间件”的设计让功能之间看起来非常简洁!笔者在项目中就曾这样使用过:

const Koa=require('koa')
const app=new Koa()
const Router=require('koa-router')
const router=new Router()
const cors=require('koa2-cors')
const koaBody=require('koa-body')

const ENV='test-mpin2'

app.use(cors({
	origin:['http://localhost:9528'],   // 也可以写为:['*']
	credentials:true
}))
app.use(koaBody({
	multipart:true
}))
app.use(async(ctx,next)=>{
	console.log('访问全局中间件')
	ctx.state.env=ENV   // 全局缓存
	await next()
})

const playlist=require('./controller/playlist.js')
router.use('/playlist',playlist.routes())
const blog=require('./controller/blog.js')
router.use('/blog',blog.routes())

app.use(router.routes()).use(router.allowedMethods())

app.listen(3000,()=>{
	console.log('服务已开启')
})

它将路由router抽离出去作为单独的中间件使用,则app只负责全局处理。还比如:

// 最外层中间件,可以用于兜底 Koa 全局错误
app.use(async (ctx, next) => {
  try {
    // 执行下一个中间件
    await next();
  } catch (error) {
    console.log(`[koa error]: ${error.message}`)
  }
});
// 第二层中间件,可以用于日志记录
app.use(async (ctx, next) => {
  const { req } = ctx;
  console.log(`req is ${JSON.stringify(req)}`);
  await next();
  console.log(`res is ${JSON.stringify(ctx.res)}`);
});

简单实现一个Koa吧!

如上代码,我们看 Koa 实例,通过use方法注册和串联中间件,其源码的简单实现可以表述为:

use(fn) {
    this.middleware.push(fn);
    return this;
}

我们将中间件存储到this.middleware数组中,那么中间件是如何被执行的呢?参考下面源码:

// 通过 createServer 方法启动一个 Node.js 服务
listen(...args) {
    const server = http.createServer(this.callback());
    server.listen(...args);
}

Koa 框架通过 http 模块的 createServer 方法创建一个 Node.js 服务,并传入 this.callback() 方法, callback源码简单实现如下:

callback(){
	const fn=compose(this.middlewareList)
	
	return (req,res)=>{
		const ctx=createContext(req,res)
		return this.handleRequest(ctx,fn)
	}
}

handleRequest(ctx, fn) {
    const onerror = err => ctx.onerror(err);
    // 将 ctx 对象传递给中间件函数 fn
    return fn(ctx).catch(onerror);
}

如上代码,我们将 Koa 一个中间件组合和执行流程梳理为以下步骤:

  • 通过一个方法(我们称为compose)组合各种中间件,返回一个中间件组合函数fn

  • 请求过来时,会先调用handleRequest方法,该方法完成:

    • 调用createContext方法,对该次请求封装出一个ctx对象;
    • 接着调用this.handleRequest(ctx, fn)处理该次请求。

其中,核心过程就是使用compose方法组合各种中间件 —— 这是一个单独的方法,它应该不受Koa其余方法的约束。其源码简单实现为:

// 组合中间件
// 和express中的next函数意义一样
function compose(middlewareList){
	// return function意思是返回一个函数
	return function(ctx,next){
		// 各种中间件调用的逻辑
		function dispatch(i){
			const fn=middlewareList[i] || next
			if(fn){
				try{
					// koa中都是async,其返回的是一个promise(对象)
					return Promise.resolve(fn(ctx,function next(){
						return dispatch(i+1)
					}))
				}catch(err){
					return Promise.reject(err)
				}
			}else{
				return Promise.resolve()
			}
		}
		return dispatch(0)
	}
}

其功能可以表示为这样(非源码):

async function middleware1() {
  //...
  await (async function middleware2() {
    //...
    await (async function middleware3() {
      //...
    });
    //...
  });
  //...
}

到这里我们其实可以“初窥”其原理,有两点:

  • Koa 的中间件机制被社区形象地总结为洋葱模型;

所谓洋葱模型,就是指每一个 Koa 中间件都是一层洋葱圈,它即可以掌管请求进入,也可以掌管响应返回。换句话说:外层的中间件可以影响内层的请求和响应阶段,内层的中间件只能影响外层的响应阶段。

  • dispatch(n)对应第 n 个中间件的执行,在使用中即第 n 个中间件可以通过await next()来“插入”执行下一个中间件,同时在最后一个中间件执行完成后,依然有恢复执行的能力。即:通过洋葱模型,await next()控制调用后面的中间件,直到全局没有可执行的中间件且堆栈执行完毕,最终“原路返回”至第一个执行next的中间件。这种方式有个优点,特别是对于日志记录以及错误处理等全局功能需要非常友好

Koa1 的中间件实现利用了 Generator 函数 + co 库(一种基于 Promise 的 Generator 函数流程管理工具),来实现协程运行。本质上,Koa v1 中间件和 Koa v2 中间件思想是类似的,只不过 Koa v2 改用了 Async/Await 来替换 Generator 函数 + co 库,整体实现更加巧妙,代码更加优雅。—— from《狼书》

经过上述部分源码的描述,我们就可以采用es6的方式将其组合起来:

// myKoa.js文件

const http=require('http')

function compose(){}   //见上

class LikeKoa2{
	constructor() {
	    this.middlewareList=[]
	}
	use(){}   //见上
	
	// 把所有的req,res属性、事件都交给ctx(这里只是简写)
	createContext(req,res){
		const ctx={
			req,
			res
		}
		// 比如
		ctx.query=req,query
		return ctx
	}
	handleRequest(){}   //见上
	callback(){}   //见上
	listen(){}   //见上
}

// koa和express的不同之一:
// express在调用时直接调用函数:const app=express();所以暴露出去new过的对象——具体见下面链接中代码
// 但是koa调用时以类的方式:const app=new Koa();所以直接暴露出去
module.exports=LikeKoa2

那use方法和其余方法并不相通,它是如何被执行的呢?执行了createServer后是不是相当于建立了一个通道、挂载了一个监听函数呢?
这一点恐怕就要到Node的源码中一探究竟了…


对比 Koa,聊聊 Express 原理

说起 Node.js 框架,我们一定忘不了 Express —— 不同于 Koa,它继承了路由、静态服务器和模板引擎等功能,虽然比之Koa显得“臃肿”了许多,但看上去比 Koa 更像是一个框架。通过学习 Express 源码,笔者简单的总结了它的工作机制:

  • 通过app.use方法注册中间件。

  • 一个中间件可以理解为一个 Layer 对象,其中包含了当前路由匹配的正则信息以及 handle 方法。

  • 所有中间件(Layer 对象)使用stack数组存储起来。

  • 当一个请求过来时,会从 req 中获取请求 path,根据 path 从stack中找到匹配的 Layer,具体匹配过程由router.handle函数实现。

  • router.handle函数通过next()方法遍历每一个 layer 进行比对:

    • next()方法通过闭包维持了对于 Stack Index 游标的引用,当调用next()方法时,就会从下一个中间件开始查找;
    • 如果比对结果为 true,则调用layer.handle_request方法,layer.handle_request方法中会调用next()方法 ,实现中间件的执行。

通过上述内容,我们可以看到,Express 其实是通过 next() 方法维护了遍历中间件列表的 Index 游标,中间件每次调用next()方法时,会通过增加 Index 游标的方式找到下一个中间件并执行。它的功能就像这样:

((req, res) => {
  console.log('第一个中间件');
  ((req, res) => {
    console.log('第二个中间件');
    (async(req, res) => {
      console.log('第三个中间件');
      await sleep(2000)
      res.status(200).send('hello')
    })(req, res)
    console.log('第二个中间件调用结束');
  })(req, res)
  console.log('第一个中间件调用结束')
})(req, res)

如上代码,Express 中间件设计并不是一个洋葱模型,它是基于回调实现的线形模型,不利于组合,不利于互操,在设计上并不像 Koa 一样简单。而且业务代码有一定程度的侵扰,甚至会造成不同中间件间的耦合。

express的简单实现笔者已上传至腾讯微云,需要者可自行查看&下载:express的简单实现

更多编程相关知识,请访问:编程视频!!

以上是比较一下nodejs中间件Koa和Express的详细内容。更多信息请关注PHP中文网其他相关文章!

声明
本文转载于:csdn。如有侵权,请联系admin@php.cn删除
node、nvm与npm有什么区别node、nvm与npm有什么区别Jul 04, 2022 pm 04:24 PM

node、nvm与npm的区别:1、nodejs是项目开发时所需要的代码库,nvm是nodejs版本管理工具,npm是nodejs包管理工具;2、nodejs能够使得javascript能够脱离浏览器运行,nvm能够管理nodejs和npm的版本,npm能够管理nodejs的第三方插件。

Vercel是什么?怎么部署Node服务?Vercel是什么?怎么部署Node服务?May 07, 2022 pm 09:34 PM

Vercel是什么?本篇文章带大家了解一下Vercel,并介绍一下在Vercel中部署 Node 服务的方法,希望对大家有所帮助!

node爬取数据实例:聊聊怎么抓取小说章节node爬取数据实例:聊聊怎么抓取小说章节May 02, 2022 am 10:00 AM

node怎么爬取数据?下面本篇文章给大家分享一个node爬虫实例,聊聊利用node抓取小说章节的方法,希望对大家有所帮助!

node导出模块有哪两种方式node导出模块有哪两种方式Apr 22, 2022 pm 02:57 PM

node导出模块的两种方式:1、利用exports,该方法可以通过添加属性的方式导出,并且可以导出多个成员;2、利用“module.exports”,该方法可以直接通过为“module.exports”赋值的方式导出模块,只能导出单个成员。

安装node时会自动安装npm吗安装node时会自动安装npm吗Apr 27, 2022 pm 03:51 PM

安装node时会自动安装npm;npm是nodejs平台默认的包管理工具,新版本的nodejs已经集成了npm,所以npm会随同nodejs一起安装,安装完成后可以利用“npm -v”命令查看是否安装成功。

聊聊V8的内存管理与垃圾回收算法聊聊V8的内存管理与垃圾回收算法Apr 27, 2022 pm 08:44 PM

本篇文章带大家了解一下V8引擎的内存管理与垃圾回收算法,希望对大家有所帮助!

聊聊Node.js path模块中的常用工具函数聊聊Node.js path模块中的常用工具函数Jun 08, 2022 pm 05:37 PM

本篇文章带大家聊聊Node.js中的path模块,介绍一下path的常见使用场景、执行机制,以及常用工具函数,希望对大家有所帮助!

node中是否包含dom和bomnode中是否包含dom和bomJul 06, 2022 am 10:19 AM

node中没有包含dom和bom;bom是指浏览器对象模型,bom是指文档对象模型,而node中采用ecmascript进行编码,并且没有浏览器也没有文档,是JavaScript运行在后端的环境平台,因此node中没有包含dom和bom。

See all articles

热AI工具

Undresser.AI Undress

Undresser.AI Undress

人工智能驱动的应用程序,用于创建逼真的裸体照片

AI Clothes Remover

AI Clothes Remover

用于从照片中去除衣服的在线人工智能工具。

Undress AI Tool

Undress AI Tool

免费脱衣服图片

Clothoff.io

Clothoff.io

AI脱衣机

AI Hentai Generator

AI Hentai Generator

免费生成ai无尽的。

热工具

ZendStudio 13.5.1 Mac

ZendStudio 13.5.1 Mac

功能强大的PHP集成开发环境

安全考试浏览器

安全考试浏览器

Safe Exam Browser是一个安全的浏览器环境,用于安全地进行在线考试。该软件将任何计算机变成一个安全的工作站。它控制对任何实用工具的访问,并防止学生使用未经授权的资源。

DVWA

DVWA

Damn Vulnerable Web App (DVWA) 是一个PHP/MySQL的Web应用程序,非常容易受到攻击。它的主要目标是成为安全专业人员在合法环境中测试自己的技能和工具的辅助工具,帮助Web开发人员更好地理解保护Web应用程序的过程,并帮助教师/学生在课堂环境中教授/学习Web应用程序安全。DVWA的目标是通过简单直接的界面练习一些最常见的Web漏洞,难度各不相同。请注意,该软件中

SublimeText3 英文版

SublimeText3 英文版

推荐:为Win版本,支持代码提示!

VSCode Windows 64位 下载

VSCode Windows 64位 下载

微软推出的免费、功能强大的一款IDE编辑器