【工程化之路】Koa 真解
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程序。
- 其实大致的用法跟
http.createserver
保持一致的app.use
中的函数 其实就是我们核心处理的逻辑.listen
表示端口监听- 其实内容很简单,所以源码也比较简单,让我们来一起分析下吧。
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上获取一些属性,但是你知道有哪些值的分类吗???
ctx.req, ctx.res
指的是原生的http的req以及res。ctx.request, ctx.response
指的框架自定义的属性,都可以在上面获取ctx.xxx
如果从ctx上获取某些属性的话,其实是通过代理的形式来从ctx.request, ctx.response
上获取的。- 接下来我们看下源码实现
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 真解相关推荐
- ds90ub934 i2c 配置_DS90UB948-Q1:1080p 双路 FPD-Link III 解串器
DS90UB948-Q1:1080p 双路 FPD-Link III 解串器 DS90UB948-Q1 是一款 FPD-Link III 解串器,与 DS90UB949/947/929-Q1 串行器配 ...
- 极客星球 | 前端工程化之路的探索与实践
前言 随着业务越来越重,越来越复杂,双倍工作量,团队规模肯定不会扩大双倍,蛮力已经无法持续高效的支持业务,如何因地制宜地打造出适合自己的前端工程化,成了每个到了一定规模的前端团队都在思考和探索的问题. ...
- 百度地图历险记之LuShu路书全解
百度地图历险记之LuShu路书全解 项目简介: 接了个双创项目的前端开发的活,主要用地图来展示一些信息,比如时间地点事件什么的.使用了Antd+react解决方案. 欢迎点个star支持下:思源siy ...
- 架构师之路(2)---详解面向过程 王泽宾
2.3 面向过程编程(OPP) 和面向对象编程(OOP)的关系 关于面向过程的编程(OPP)和面向对象的编程(OOP),给出这它们的定义的人很多,您可以从任何资料中找到很专业的解释,但以我的经验来看, ...
- 删库不跑路,详解MySQL数据恢复
日常工作中,总会有因手抖.写错条件.写错表名.错连生产库造成的误删库表和数据的事情发生.那么,如果连数据都恢复不了,还要什么 DBA >>>> 1 前言 数据恢复的前提的做好备 ...
- 微信小程序工程化之路
前言 最近很长一段时间都在和前端项目构建"纠缠"在一起,处理了web项目.app项目紧接着便是微信小程序,之所以把微信单独拎出来做一篇分享,主要是因为小程序的工程化在很多细节上是区 ...
- 【通信原理 入坑之路】—— 详解IQ调制以及星座图原理
写在前面:本博客是<深入浅出通信原理>的学习笔记,仅供个人学习参考使用 文章目录 一. IQ调制与解调的原理与过程 1.1 利用旋转向量理解IQ调制(正交调制) 1.2 利用旋转向量理解I ...
- Fis3的前端工程化之路[三大特性篇之资源定位]
Fis3版本:v3.4.22 Fis3的三大特性 资源定位:获取任何开发中所使用资源的线上路径 内容嵌入:把一个文件的内容(文本)或者base64编码(图片)嵌入到另一个文件中 依赖声明:在一个文本文 ...
- [原创]java WEB学习笔记58:Struts2学习之路---Result 详解 type属性,通配符映射
本博客的目的:①总结自己的学习过程,相当于学习笔记 ②将自己的经验分享给大家,相互学习,互相交流,不可商用 内容难免出现问题,欢迎指正,交流,探讨,可以留言,也可以通过以下方式联系. 本人互联网技术爱 ...
最新文章
- Logback日志配置(分级别输出到不同文件)
- 机房墙面为什么要做保温?该怎么做?
- 部署Office Web Apps Server并配置其与SharePoint 2013的集成
- m4a打开服务器运行失败,WINCC打不开项目,服务器运行失败
- no result type defined for type 'dispatch'mapped
- UIAutomator简介
- 三个好用的并发工具类
- HDU 1402 A * B Problem Plus FFT
- python私有属性怎么定义_Python中定义私有属性的方法是()。
- VisualVM远程监控Java
- 文末送书 | 自动机器学习(AutoML):方法、系统与挑战
- 三、常用行内元素与块元素
- 【ACL2020】DeeBERT:衡量性能与效率的 BERT 推理方法
- Centos系统安装masscan
- 全球货币市场基础知识系列4
- 【微信小程序】canvas生成分享图片海报模糊解决方法
- OCO-2卫星数据批量化下载教程
- Echart 仪表盘 样式调整
- 免费的视频服务器空间
- Python数据分析与机器学习45- 股票预测
热门文章
- pr 增加配置文件和级别_premiere渲染设置里的级别是什么意思?
- 微信群中实现点歌QQ音乐功能 微群宝贝点歌
- pikachu+SQL注入+Burp Suit
- 在微信小程序中如何使用wx.onLocationChange(function callback)
- Robocup 仿真2D 学习笔记(四)阵型编辑
- 高中学历的我,为什么放弃医院的工作转行互联网?
- 内核中line discipline的注册流程以及BT hciattach进程的启动
- 月入8000+的steam搬砖项目,你应该知道(详细教程)
- [升级版][Java作业]创建PC对象展示cpu速度和硬盘容量
- 特别研究:资产证券化——REITs