搜索
首页web前端js教程比较一下nodejs中间件Koa和Express

比较一下nodejs中间件Koa和Express

Feb 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删除
JavaScript和Web:核心功能和用例JavaScript和Web:核心功能和用例Apr 18, 2025 am 12:19 AM

JavaScript在Web开发中的主要用途包括客户端交互、表单验证和异步通信。1)通过DOM操作实现动态内容更新和用户交互;2)在用户提交数据前进行客户端验证,提高用户体验;3)通过AJAX技术实现与服务器的无刷新通信。

了解JavaScript引擎:实施详细信息了解JavaScript引擎:实施详细信息Apr 17, 2025 am 12:05 AM

理解JavaScript引擎内部工作原理对开发者重要,因为它能帮助编写更高效的代码并理解性能瓶颈和优化策略。1)引擎的工作流程包括解析、编译和执行三个阶段;2)执行过程中,引擎会进行动态优化,如内联缓存和隐藏类;3)最佳实践包括避免全局变量、优化循环、使用const和let,以及避免过度使用闭包。

Python vs. JavaScript:学习曲线和易用性Python vs. JavaScript:学习曲线和易用性Apr 16, 2025 am 12:12 AM

Python更适合初学者,学习曲线平缓,语法简洁;JavaScript适合前端开发,学习曲线较陡,语法灵活。1.Python语法直观,适用于数据科学和后端开发。2.JavaScript灵活,广泛用于前端和服务器端编程。

Python vs. JavaScript:社区,图书馆和资源Python vs. JavaScript:社区,图书馆和资源Apr 15, 2025 am 12:16 AM

Python和JavaScript在社区、库和资源方面的对比各有优劣。1)Python社区友好,适合初学者,但前端开发资源不如JavaScript丰富。2)Python在数据科学和机器学习库方面强大,JavaScript则在前端开发库和框架上更胜一筹。3)两者的学习资源都丰富,但Python适合从官方文档开始,JavaScript则以MDNWebDocs为佳。选择应基于项目需求和个人兴趣。

从C/C到JavaScript:所有工作方式从C/C到JavaScript:所有工作方式Apr 14, 2025 am 12:05 AM

从C/C 转向JavaScript需要适应动态类型、垃圾回收和异步编程等特点。1)C/C 是静态类型语言,需手动管理内存,而JavaScript是动态类型,垃圾回收自动处理。2)C/C 需编译成机器码,JavaScript则为解释型语言。3)JavaScript引入闭包、原型链和Promise等概念,增强了灵活性和异步编程能力。

JavaScript引擎:比较实施JavaScript引擎:比较实施Apr 13, 2025 am 12:05 AM

不同JavaScript引擎在解析和执行JavaScript代码时,效果会有所不同,因为每个引擎的实现原理和优化策略各有差异。1.词法分析:将源码转换为词法单元。2.语法分析:生成抽象语法树。3.优化和编译:通过JIT编译器生成机器码。4.执行:运行机器码。V8引擎通过即时编译和隐藏类优化,SpiderMonkey使用类型推断系统,导致在相同代码上的性能表现不同。

超越浏览器:现实世界中的JavaScript超越浏览器:现实世界中的JavaScriptApr 12, 2025 am 12:06 AM

JavaScript在现实世界中的应用包括服务器端编程、移动应用开发和物联网控制:1.通过Node.js实现服务器端编程,适用于高并发请求处理。2.通过ReactNative进行移动应用开发,支持跨平台部署。3.通过Johnny-Five库用于物联网设备控制,适用于硬件交互。

使用Next.js(后端集成)构建多租户SaaS应用程序使用Next.js(后端集成)构建多租户SaaS应用程序Apr 11, 2025 am 08:23 AM

我使用您的日常技术工具构建了功能性的多租户SaaS应用程序(一个Edtech应用程序),您可以做同样的事情。 首先,什么是多租户SaaS应用程序? 多租户SaaS应用程序可让您从唱歌中为多个客户提供服务

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无尽的。

热门文章

R.E.P.O.能量晶体解释及其做什么(黄色晶体)
1 个月前By尊渡假赌尊渡假赌尊渡假赌
R.E.P.O.最佳图形设置
1 个月前By尊渡假赌尊渡假赌尊渡假赌
威尔R.E.P.O.有交叉游戏吗?
1 个月前By尊渡假赌尊渡假赌尊渡假赌

热工具

MinGW - 适用于 Windows 的极简 GNU

MinGW - 适用于 Windows 的极简 GNU

这个项目正在迁移到osdn.net/projects/mingw的过程中,你可以继续在那里关注我们。MinGW:GNU编译器集合(GCC)的本地Windows移植版本,可自由分发的导入库和用于构建本地Windows应用程序的头文件;包括对MSVC运行时的扩展,以支持C99功能。MinGW的所有软件都可以在64位Windows平台上运行。

SublimeText3 英文版

SublimeText3 英文版

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

SublimeText3汉化版

SublimeText3汉化版

中文版,非常好用

适用于 Eclipse 的 SAP NetWeaver 服务器适配器

适用于 Eclipse 的 SAP NetWeaver 服务器适配器

将Eclipse与SAP NetWeaver应用服务器集成。

PhpStorm Mac 版本

PhpStorm Mac 版本

最新(2018.2.1 )专业的PHP集成开发工具