1. 前言

昨天花费了比较多的时间将Koa的源码阅读了一遍,主要是项目中用到了Koa,为了做的更加得心应手所以先将源码看一下,总体上源码还是非常简单的,没啥难度。一方面为了总结另一方面也是为了不太看懂源码的同学们,今天我会好好的写总结下,供大家学习。

2. 基础用法

const Koa = require("koa");
const app = new Koa();app.use( (ctx) => {ctx.body = "hello wold"
});app.listen(3001, () => {console.log("服务启动成功")
})

以上简单的代码就是基于Koa的web程序。

  1. 其实大致的用法跟http.createserver保持一致的
  2. app.use中的函数 其实就是我们核心处理的逻辑
  3. .listen 表示端口监听
  4. 其实内容很简单,所以源码也比较简单,让我们来一起分析下吧。

3. koa 分析

3.1 核心文件

  • application Koa框架 入口级文件
  • context Koa context上下文 文件
  • request Koa request 请求文件
  • response Koa response 响应文件
  • koa-componse Koa 实现中间件串行的文件(我从别的源码中扒拉过来的)
  • delegates Koa 实现ctx 属性代理的文件(我从别的源码中扒拉过来的)

3.2 构造初期化

其实文件application.js 是我们应用的入口。我们在开发过程中导出Koa,其实就是导出这个文件。通过下图可以看出。

其实在Koa初期化的过程中,还是做了一些特殊的处理,接下来让我们看下。


源码位置

上述代码的作用在于:解决多个应用存在的问题,如下代码

const Koa = require("koa")
const app = new Koa();
const app1 = new Koa();
const app2 = new Koa();app.context.test = 1;

如果给一个app实例上赋值,那么所有的实例都会存在相同的值,所以使用Object.create 用来进行属性隔离的。

3.3 ctx特有属性

在使用中间件的时候,我们一般都会在ctx上获取一些属性,但是你知道有哪些值的分类吗???

  1. ctx.req, ctx.res 指的是原生的http的req以及res。
  2. ctx.request, ctx.response 指的框架自定义的属性,都可以在上面获取
  3. ctx.xxx 如果从ctx上获取某些属性的话,其实是通过代理的形式来从ctx.request, ctx.response上获取的。
  4. 接下来我们看下源码实现
  createContext (req, res) {// 为了每次请求都会有一个新上下文内容 所以每次请求都会进行Object.createconst context = Object.create(this.context)const request = context.request = Object.create(this.request)const response = context.response = Object.create(this.response)// 下面赋值的目的在于 "你中有我,我中有你"context.app = request.app = response.app = thiscontext.req = request.req = response.req = reqcontext.res = request.res = response.res = resrequest.ctx = response.ctx = contextrequest.response = responseresponse.request = requestcontext.originalUrl = request.originalUrl = req.urlcontext.state = {}return context}

上述代码中实现了你中有我,我中有你。 会将context, req, res, request, response 互相关联。

3.3.1 QA

  • 问题: 为什么在函数createContext中会有Object.create呢???
  • 解答: 每次请求过来,开始执行中间件的时候都会调用此方法,所以每次都会生成一个新的ctx。从Koa的角度来看,每个请求都应该是一个全新的,不应该跟之前的有关联。 所以如果在中间件运行过程中给ctx挂载一个属性,只能给后面的中间件使用,无法做到下次请求使用

3.4 中间件核心原理

首先我们看下 添加中间件的本质:

  use (fn) {// fn 必须是一个函数。 反之就会保存if (typeof fn !== 'function') throw new TypeError('middleware must be a function!')// 添加到数组中。 返回this。 可以进行use链this.middleware.push(fn)return this}

其实中间件use的部分无非是 将中间件函数添加到数组中。 每次都会串行执行所有的函数。那就让我们看来下 是如何串行的

return function (context, next) {let index = -1;return dispatch(0);function dispatch(i) {index = i;let fn = middleware[i];// 此处就是将返回结果包裹Promisereturn Promise.resolve(fn(context, dispatch.bind(null, i + 1)));}
};
  • 通过上述的代码其实我们可以看到,源码中无非是通过递归调用函数,将每个函数串行。
  • 函数fn中第二个参数dispatch,其实就是我们平常使用的next函数。 如果不手动调用next,就不会执行下一个函数。
  • 而且函数外面 我们其实是用Promise.reoslve包裹,所以内部使用async await 是一定没有错误的。不然会出现意向不到的错误。

3.4.1 正确以及错误的实例

  • 正确实例
app.use(async (ctx, next) => {console.log(1)await next();console.log(4)
})
app.use(async (ctx, next) => {console.log(2)await next();console.log(5)
})
app.use(async (ctx, next) => {console.log(3)await next();console.log(6)
})
// 1 2 3 6 5 4
  • 错误实例
app.use(async (ctx, next) => {console.log(1)await next();console.log(4)
})
app.use(async (ctx, next) => {console.log(2)next();console.log(5)
})
app.use(async (ctx, next) => {console.log(3)await next();console.log(6)
})
// 1 2 3 5 6 4

3.5 ctx代理

其实在使用中间件的过程中,我们可以通过ctx.headers 来获取值,那么ctx真的会定义此属性吗??? 源码我就不进行粘贴了。看下分析源码

其实只要有一定js代码基础的知道,我们可以通过proxy, ‘Object.defineProperty’, ‘__defineGetter__’ 来进行代理。

而从ctx上获取值的原理也很简单:通过代理的形式,在使用ctx.属性 的时候,从request, ‘response’ 来获取值 从而返回。设置值 是同样如此的。

4. 结论:

Koa的源码部分就分析到这里,其实都很简单没啥难度的。如果大家有什么不懂的,或是 有什么新的建议,可以在评论区及时跟我留言啊。

【工程化之路】Koa 真解相关推荐

  1. ds90ub934 i2c 配置_DS90UB948-Q1:1080p 双路 FPD-Link III 解串器

    DS90UB948-Q1:1080p 双路 FPD-Link III 解串器 DS90UB948-Q1 是一款 FPD-Link III 解串器,与 DS90UB949/947/929-Q1 串行器配 ...

  2. 极客星球 | 前端工程化之路的探索与实践

    前言 随着业务越来越重,越来越复杂,双倍工作量,团队规模肯定不会扩大双倍,蛮力已经无法持续高效的支持业务,如何因地制宜地打造出适合自己的前端工程化,成了每个到了一定规模的前端团队都在思考和探索的问题. ...

  3. 百度地图历险记之LuShu路书全解

    百度地图历险记之LuShu路书全解 项目简介: 接了个双创项目的前端开发的活,主要用地图来展示一些信息,比如时间地点事件什么的.使用了Antd+react解决方案. 欢迎点个star支持下:思源siy ...

  4. 架构师之路(2)---详解面向过程 王泽宾

    2.3 面向过程编程(OPP) 和面向对象编程(OOP)的关系 关于面向过程的编程(OPP)和面向对象的编程(OOP),给出这它们的定义的人很多,您可以从任何资料中找到很专业的解释,但以我的经验来看, ...

  5. 删库不跑路,详解MySQL数据恢复

    日常工作中,总会有因手抖.写错条件.写错表名.错连生产库造成的误删库表和数据的事情发生.那么,如果连数据都恢复不了,还要什么 DBA >>>> 1 前言 数据恢复的前提的做好备 ...

  6. 微信小程序工程化之路

    前言 最近很长一段时间都在和前端项目构建"纠缠"在一起,处理了web项目.app项目紧接着便是微信小程序,之所以把微信单独拎出来做一篇分享,主要是因为小程序的工程化在很多细节上是区 ...

  7. 【通信原理 入坑之路】—— 详解IQ调制以及星座图原理

    写在前面:本博客是<深入浅出通信原理>的学习笔记,仅供个人学习参考使用 文章目录 一. IQ调制与解调的原理与过程 1.1 利用旋转向量理解IQ调制(正交调制) 1.2 利用旋转向量理解I ...

  8. Fis3的前端工程化之路[三大特性篇之资源定位]

    Fis3版本:v3.4.22 Fis3的三大特性 资源定位:获取任何开发中所使用资源的线上路径 内容嵌入:把一个文件的内容(文本)或者base64编码(图片)嵌入到另一个文件中 依赖声明:在一个文本文 ...

  9. [原创]java WEB学习笔记58:Struts2学习之路---Result 详解 type属性,通配符映射

    本博客的目的:①总结自己的学习过程,相当于学习笔记 ②将自己的经验分享给大家,相互学习,互相交流,不可商用 内容难免出现问题,欢迎指正,交流,探讨,可以留言,也可以通过以下方式联系. 本人互联网技术爱 ...

最新文章

  1. Logback日志配置(分级别输出到不同文件)
  2. 机房墙面为什么要做保温?该怎么做?
  3. 部署Office Web Apps Server并配置其与SharePoint 2013的集成
  4. m4a打开服务器运行失败,WINCC打不开项目,服务器运行失败
  5. no result type defined for type 'dispatch'mapped
  6. UIAutomator简介
  7. 三个好用的并发工具类
  8. HDU 1402 A * B Problem Plus FFT
  9. python私有属性怎么定义_Python中定义私有属性的方法是()。
  10. VisualVM远程监控Java
  11. 文末送书 | 自动机器学习(AutoML):方法、系统与挑战
  12. 三、常用行内元素与块元素
  13. 【ACL2020】DeeBERT:衡量性能与效率的 BERT 推理方法
  14. Centos系统安装masscan
  15. 全球货币市场基础知识系列4
  16. 【微信小程序】canvas生成分享图片海报模糊解决方法
  17. OCO-2卫星数据批量化下载教程
  18. Echart 仪表盘 样式调整
  19. 免费的视频服务器空间
  20. Python数据分析与机器学习45- 股票预测

热门文章

  1. pr 增加配置文件和级别_premiere渲染设置里的级别是什么意思?
  2. 微信群中实现点歌QQ音乐功能 微群宝贝点歌
  3. pikachu+SQL注入+Burp Suit
  4. 在微信小程序中如何使用wx.onLocationChange(function callback)
  5. Robocup 仿真2D 学习笔记(四)阵型编辑
  6. 高中学历的我,为什么放弃医院的工作转行互联网?
  7. 内核中line discipline的注册流程以及BT hciattach进程的启动
  8. 月入8000+的steam搬砖项目,你应该知道(详细教程)
  9. [升级版][Java作业]创建PC对象展示cpu速度和硬盘容量
  10. 特别研究:资产证券化——REITs