深入node.js koa原理,实现koa

express 和koa 的对比

  • express源码是基于es5写的,koa是基于es6写的。
  • express比较全,内置了很多功能,koa内部核心非常小巧,需要通过扩展的插件来进行扩展
  • express 和koa都hi可以自己实现mvc功能
  • express处理异步都是回调函数,而koa处理异步都是async + await

koa

koa的ctx上下文提供很多比如使用http模块中的req通过解析才能获取的pathname,koa中直接通过ctx.path获取。因为底层还是http,还是通过封装http模块的一些东西,让我们可以更加简便的的使用,而不用像使用http模块那么费劲。
在这里插入图片描述

实现一个koa

在这里插入图片描述
目录保持一致。主要实现use listen方法
在这里插入图片描述
底层还是基于http模块,在这里插入图片描述
listen方法还是创建一个server。在这里插入图片描述
use方法存放中间件,相当于订阅。在这里插入图片描述
每次请求来了之后就处理请求,然后处理订阅的中间件,有点像发布订阅的模式。在这里插入图片描述

  • 这里的createContext是用来创建ctx的。这里做了两层隔离。最开始是constructor的隔离,避免多个new Koa的时候,他们改变的context是一样的。
    第二层就是每次请求的时候,都会生成全新的ctx,这里也需要做隔离处理,避免不同请求的ctx是一样的,这样隔离了,每个实例的ctx都是全新的对象,每个请求的ctx也是全新的对象。源码中也是这么处理的。
    在这里插入图片描述
    在这里插入图片描述
  • 接着实现koa的上下文ctx。
    在这里插入图片描述
    先将原生的req和res挂载到ctx的requests和response上去。此时就可以通过ctx.req做原生的处理。接着处理request,如果从ctx.request.xx获取值,
    在这里插入图片描述
  • request里面采用了Object.defineProperty的简单写法,比如get url(){}就是
    Object.defineProperty(request, ‘url’, {get(){}})这样。然后每次通过ctx.request.xx获取值的时候,会通过原型链找到这里,调用url的get方法,此时里面的this指向的是ctx.request,而刚刚把原生req挂载到了ctx.request上面了,所以就可以通过this.req来获取一些值,甚至可以做一些处理,如path,query等等,这也是req挂载到ctx.reqeust上的原因。
  • 这时候就可以通过ctx.request.query获取值了,然后ctx想获取值,就需要在座一层代理,将ctx.xx代理到ctx.request.xx上。在这里插入图片描述
    正常还是这么做就行了,但是太多个写起来很费劲,源码是采用了Object.prototype._ _defineGetter__来完成的。在这里插入图片描述
    我们这里简单模拟一下,代理context,调用 _ _defineGetter__方法,然后对每个Key做代理,比如ctx.url,当我们访问了Ctx.url之后,就会走里面的逻辑,此时this指向的是Ctx,所以可以通过this.request.url去返回。这样就做了一层代理。
    简单的koa就实现了。在这里插入图片描述
    在这里插入图片描述
    可以正常获取到。

响应体的处理

响应体koa实现了ctx.body,效果相当于res.end。
ctx.body也代理到了ctx.response.body去了。如:
在这里插入图片描述
response对象有body,属性,然后
在这里插入图片描述
ctx做了代理,访问ctx.body的时候返回的事ctx.response.body。
设置ctx.body的时候是设置了ctx.response.body。所以说到底还是对ctx.response.body的处理。
代理之后,我们操作了ctx.body之后,在中间件执行完毕之后就需要对body做处理。
在这里插入图片描述
this,compose是用来执行中间件的,先不管,当中间件执行完毕之后,就对body做处理了,然后调用res.end。所以本质还是调用res.end,只是封装了很多东西,可以直接返回object等,res.end是不可以直接返回object的。响应体的处理就是这样。

中间件的执行

koa一大特点跟express不一样的是,中间件可以使用async await,而不是回调的形式。可以执行多个中间件,通过next调用。洋葱模型~。

如图,从第一个中间件进去,打印1.然后执行next。会立马执行第二个中间件,打印2,然后执行next执行第三个中间件,打印3。然后第三个中间件执行完毕后再往回走,依次执行2,1。可以理解成从入口进入,一定会从出口出来。
但是如果next里面有异步操作。如:
在这里插入图片描述
r如图,第二个中间件执行的时候,用了await,后面的代码需要等待p的状态完成,而异步操作,我们第一个中间件并不会等待他,然后又返回打印1。如,在这里插入图片描述
只打印1,2,1。
所以每个中间件最好使用await去执行next,这样才能保证每个中间件的内容会完全执行。
基于这个思路完成中间件的改造:
在这里插入图片描述
首先将每个订阅的中间件存起来。
等待请求来的时候再处理,思路就是:
封装一个dispatch函数,然后将采用递归,从第一个中间件开始执行,将dispatch函数作为next传入,索引+1,当第一个中间件执行next的时候,就会执行dispatch,执行第二个中间件,以此类推,直到所有中间件执行完毕。并且每个中间件都必须是返回pormise的函数,这样才能使用await。
在这里插入图片描述
简单的几行代码,贯穿koa中间件的执行原理。源码也是类似这样实现。之不过稍微复杂了点。
在这里插入图片描述
将ctx传入,然后通过索引i,
在这里插入图片描述
相当于第一次直接调用了next()去执行第一个中间件,为什么不用await,因为直接return,会等待dispatch(0)执行完毕之后再往下走。
然后第一个进去就执行了第一个中间件,并且将索引+1赋值给dispatch,作为nextf赋值给下一个中间件。以此类推,到最后中间件调用next的时候,i已经等于length了,就直接返回成功,然后再往回走。相当于洋葱模型,这样就完成了对koa中间件的处理,

错误处理

koa内部还继承了events模块,可以更加优雅的处理报错,比如执行中间件promise的时候抛错或者报错,都会走catch,然后通过events.emit(‘error’)将错误传递出去,
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在外部就可以通过app.on('error‘)事件统一处理错误。

最后写一个解析请求体的中间件。

中间件一般都是返回一个函数,采用柯里化。比如解析请求体,解析请求然后将数据塞入到ctx.request.body里面。在这里插入图片描述
如图,每次请求都会走这个中间件,然后将请求体解析之后塞入到了ctx.request.body里面,然后继续next往下走。
中间件的本质是用来加强koa的功能。
这样简单的koa功能就实现完毕了。

总结:

koa的优点是:中间件(洋葱模型),错误处理机制采用events,在原生的req, res之上封装了更多东西。

  • koa的实现其实就是封装了http模块,实现了一个全新的ctx上下文,该上下文中有原生的req,res,也有koa自己封装的request, response
  • 为了让request有更丰富的功能,将req挂载到了request上面,并且做了一层代理,采用Object.defineProperty的加简写,对requesst做了一层代理,比如访问ctx.request.url,实际上就是访问ctx.request.req.url,访问原生的url,不仅如此,在原生有的属性上,request借助一些比如url模块,采用url.parse去解析,使request的功能更加丰富。
  • 然后也是采用同样的代理,将Ctx代理到ctx.request上,采用的是Object.prototype. _ _DefineGetter__的方法,因为Object.defineProperty的set方法需要借助get方法。通过代理,比如访问ctx.xx就会代理到ctx.request.xx上面去,将ctx.request.xx返回。
  • 响应体的处理跟request一样的,也是通过代理,ctx.body代理到了ctx.request.body上,设置和获取其实都是处理ctx.response.body。然后在所有中间件执行完毕之后对ctx.body做处理,比如解析他的类型,设置对应请求头,然后通过res.end去返回,本质也是使用htt模块的res.end。
  • koa重要的是一个中间件的处理,koa支持了async + await的中间件,像洋葱一样,可以等待中间件执行完毕之后往下走,而express不可以。
  • 实现思路就是将所有中间件整合成一个promise,然后从第一个中间件开始执行,索引+1,调用next的时候又会执行第二个中间件,索引+1,直到最后调用完毕,而且每个中间件都会被包装成promise,.resolve去执行,这样就能搭配async 和await,等到所有中间件执行完毕又会一层一层往回执行,就相当于洋葱模型一样。
  • 此外,koa还继承了Events模块,在通过promise捕获到错误的时候可以使用事件触发的形式传递错误,使我们编写代码更加优雅。
  • 这就是koa的实现原理,封装,代理等等。
  • 2
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

coderlin_

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值