koa2中间件个人理解

首先我们要搞清楚我们使用koa框架到底要干一件什么事情
我们要干的事情就是前端向服务器发送HTTP请求来获取数据,我们要能在Koa中接收到该HTTP请求,并作处理,然后将数据返回。那么如何接收HTTP请求呢,这个时候就要用到koa中的中间件的概念。

乍一听,有点懵圈,中间件是个什么鬼东西,莫慌莫慌,大家只需要把它理解成一个函数就完事了,编写一个中间件,就和你编写一个函数是一样的

举个最简单的例子
你想前端在发送http请求之后调用一个你自己写的函数如下(随便建立一个js文件,文件名大家自己可以随意起,我这个地方的文件名叫做app.js)

const Koa = require('koa') //将安装的koa包导入到当前文件中

const app = new Koa()   //实例化一个koa应用程序对象

function test(){
	console.log('hello,yu')
}

我们要做的首先就是要把这个test函数注册到koa应用程序对象上,那么如何注册呢,也很简单

const Koa = require('koa') //将安装的koa包导入到当前文件中

const app = new Koa()   //实例化一个koa应用程序对象

function test(){
	console.log('hello,yu')
}

app.use(test)        //将test函数注册到koa应用程序对象上

这样我们编写的函数function就变成一个中间件,等待下次发起http请求的时候就可以执行了,是不是很简单!
但是通常情况下我们是不会把我们写的这样一个函数的函数名注册到koa里面的,我们一般是直接这样写

const Koa = require('koa') //将安装的koa包导入到当前文件中

const app = new Koa()   //实例化一个koa应用程序对象

app.use(()=>{
	console.log('hello,yu')
})                                    //将函数注册到koa应用程序对象上

这种写法是和上面的那种写法是完全等效的。(上面的箭头函数的写法是ES6的基础,不懂得请自行恶补ES6)我们在终端中输入命令node app.js ,你会发现打印出了hello,yu

那么问题又来了,一个应用应用程序对象上面只能有一个中间件吗?当然肯定不是的呀,一个应用程序对象是可以注册多个中间件的,接着上面的例子,我们再注册一个(当然你也可以注册好多个)

const Koa = require('koa') //将安装的koa包导入到当前文件中

const app = new Koa()   //实例化一个koa对象

app.use(()=>{
	console.log('hello,yu')
})                                    //将函数注册到koa应用程序对象上


app.use(()=>{
	console.log('hello,yuyu')          //再注册一个中间件
}) 

当我们调用的时候,你发现怎么只打印出了上面那个,下面那个怎么没有打印出来,这是为森么呢,这是因为上面的那个是先注册的,下面的那个是后注册的,在koa中它只会自动帮你执行第一个中间件,后续的它都不管的,后面的中间件都需要开发者自己去调用的,那我们如何能够确保我们注册在应用程序对象上的中间件都能执行呢?koa已经帮我们想好啦,koa在执行每一个中间件的时候都会给这个中间件的函数传递两个参数,第一个为ctx(koa中叫上下文),第二个为next(就是指下一个中间件函数),这两个参数是由koa内部的机制帮我们自动传入的,不需要我们开发者自己管理的。我们刚刚不是只调用了第一个吗,这个时候我们只要用next就可以进行第二个的调用啦,我们只需要在

const Koa = require('koa') //将安装的koa包导入到当前文件中

const app = new Koa()   //实例化一个koa对象

app.use((ctx,next)=>{
	console.log('hello,yu')
	next()                             //调用下一个中间件函数
})                                    //将函数注册到koa应用程序对象上


app.use((ctx,next)=>{
	console.log('hello,yuyu')
}) 

这个时候我们就能打印出两个函数中的内容啦。这样就完事啦!基本上koa的所有编程都是围绕着ctxnext两个东西展开的。
看起来是不是很简单,是不是就像是两个函数之间的相互调用一样。
接下来我们让这个中间件变得稍微复杂一点

const Koa = require('koa') //将安装的koa包导入到当前文件中

const app = new Koa()   //实例化一个koa对象

app.use((ctx,next)=>{
	console.log('1')
	next()
	console.log('2')
})                                    //将函数注册到koa应用程序对象上


app.use((ctx,next)=>{
	console.log('3')
	next()
	console.log('4')
}) 

我们大胆猜测一下打印的顺序是啥,是不是不难猜出顺序是1、3、4、2,我们执行之后发现是没错的,至于说为啥3完了之后是4呢,因为我们只定义了两个,第二个中间件找不到下一个中间件,所以第二个中间件的next这个地方可以先忽略。那么这个简单的函数示例就印证了koa著名的洋葱模型啦。
每个中间件函数被next分割成了上半部分和下半部分,当一个请求进来之后先执行fun1的上半部分,接着是fun2的上半部分,再然后是fun2的下半部分,最后是fun1的下半部分,这就是所谓的洋葱模型,网上也有很多资料,随便给大家找了张图
koa2中间件个人理解_第1张图片那么next返回的究竟是一个什么东西我们可以打印看一下

const Koa = require('koa') //将安装的koa包导入到当前文件中

const app = new Koa()   //实例化一个koa对象

app.use((ctx,next)=>{
	const a = next()
	console.log(a)
})                                    //将函数注册到koa应用程序对象上


app.use((ctx,next)=>{
	next()
}) 

我们会发现a是一个promise对象,但是里面是undefined,为啥呢,这是因为第二个中间件没有返回任何的值给第一个中间件,我们稍微做一下改动

const Koa = require('koa') //将安装的koa包导入到当前文件中

const app = new Koa()   //实例化一个koa对象

app.use((ctx,next)=>{
	const a = next()
	console.log(a)
})                                    //将函数注册到koa应用程序对象上


app.use((ctx,next)=>{
	next()
	return '123'
}) 

这个时候a的值就变成了Promise{‘123’}了,这个字符串123被强制包装成了一个promise,所以说中间件之间的调用总会会返回一个pormise。
那么问题又来了,我们有时候就想要接收到的结果就直接是我返回的字符串123,而不是被包装过的promise{‘123’},那这个时候就用到了async和await
我们只要将代码改成

app.use(async(ctx,next)=>{
	const a = await next()
	console.log(a)
})                                    //将函数注册到koa应用程序对象上


app.use((ctx,next)=>{
	next()
	return '123'
}) 

这样我们就可以直接获取到字符串123啦
这也就是为啥大家在网上搜索资料的时候都会发现中间件的写法是这样的

app.use(async(ctx,next)=>{
	console.log('3')
	await next()
	console.log('4')
}) 

不难看出,在函数名前面加了async,在next前面加了await,如果大家不想深入的知道为什么要加,大家只需要记住必须要这么写,这样能确保你在写中间件的时候是万无一失的,记死就完事了。
下面说一下为啥子,我们在使用nodejs编程的时候,绝大多数都是异步编程,强制在中间件加上async和await能保证所有的中间件都是按照洋葱模型执行的。那么肯定有人又要问了,为啥一定要按照洋葱模型执行,这个肯定是有原因呀,不然洋葱模型设计出来干嘛,。。
假设我们在第一个中间件函数里面要执行一段逻辑代码,它有个前提条件,必须要等后面全部的中间件函数执行完毕之后它才调用(最常见的比如说定时器),这个时候我们怎么保证后面的中间件函数执行完毕呢,这个时候不就用到洋葱模型了。如果不加async和await,可能就会导致你的代码在执行过程中出现某些错误。解释完毕。

在上面我们仅仅谈到了next,下面我们说一下ctx的用法。假设第一个中间件函数要接收第二个中间件函数传递给它的一些参数,正常情况下是不是将参数return回去就可以了,这样写当然没毛病,但是这样的传参方式是有局限性的,这个局限性在于如果几个中间件都是你自己编写的,你能够完全掌控这些中间件之间的调用顺序,确实这样写是没啥毛病的,但是在实际的开发工作中,我们常常会用到一些第三方的中间件,这些第三方的中间件,你是啷个知道它的调用顺序的耶,那这个时候的传参,就肯定不能用return了是不。

举个例子,我用了三个中间件,第一个中间件和第三个中间件都是自己写的,第二个中间件是调用了第三方的,我现在第一个中间件要获取到第三个里面的参数,ok,你第三个中间件return回参数没毛病给第二个中间件,但你第二个中间件啷个return给第一个中间件呢?你又没办法修改第三方的代码,这个时候ctx就光荣登场了,我们只需要把第三个中间件的参数绑定到ctx上,如下

const Koa = require('koa') //将安装的koa包导入到当前文件中

const app = new Koa()   //实例化一个koa对象

app.use(async(ctx,next)=>{
	await next()
	const r = ctx.r 
	console.log(r)
})                                    //将函数注册到koa应用程序对象上


app.use(async(ctx,next)=>{
	await next()
	ctx.r = 'abc'
}) 

这样就可以啦,但是这样的传值方式务必要保证中间件是执行顺序是按照洋葱模型来执行的,这就呼应到了上面说的在写中间件的时候一定要在函数名前面加asyncnext前面一定要加await,如果你要是不相信,你把第一个中间件的awaitasync取消掉,你可以看看打印出来的是啥,你验证之后就会发现打印出来的是undefined

好啦,今天就先写在这里啦~

你可能感兴趣的:(koa2中间件个人理解)