未加星标

通过debugger学习Koa2源码第一篇

字体大小 | |
[前端(javascript) 所属分类 前端(javascript) | 发布者 店小二05 | 时间 2019 | 作者 红领巾 ] 0人收藏点击收藏

个人觉得学习源码需要带着目的去看, 才能达到效果, 但是公司又没有上Node, 没有实践怎么办呢?最近发现通过调试Koa2源码也是个不错的法子。

准备工作 - 安装node - 安装vscode - 学习如何在vscode下调试 复制代码

关于Node下调试推荐阅读下: 《Node.js 调试指南》 。

我们这里只需要学习 如何在vscode下调试 即可。 具体就不详情说了, 见链接, 有问题我们可以讨论。

从Hello World开始学习 // 安装koa2, nodemon等后, 来个入门的hello world const Koa = require('koa') const app = new Koa() const port = 3000 app.use(async (ctx, next) => { await next() ctx.response.status = 200 ctx.response.body = 'hello world' }) app.listen(port, () => { console.log(`server is running on the port: ${port}`) }) 复制代码

是的没错,通过上述这个入门代码也能学习到Koa2的源码知识。

首先观察上面用到了的一些API。 new Koa() app.use() app.listen() 复制代码

我们现在开始进入node_modules目录下找到Koa。


通过debugger学习Koa2源码第一篇

通过package.json得知Koa的入口文件为

"main": "lib/application.js" 复制代码 // lib目录下模块 - application.js 实例 - context.js 上下文对象 - request.js 请求对象 - response.js 响应对象 复制代码

现在我们当然从入口文件application.js开始。我们的实例代码第一行是new koa(); 我们肯定是有一个类,这里是开始的突破点。

// 构造函数继承node的EventEmitter类 // http://nodejs.cn/api/events.html module.exports = class Application extends Emitter { ... } 复制代码

然后我们去打三个断点, 分别如下:


通过debugger学习Koa2源码第一篇
通过debugger学习Koa2源码第一篇
通过debugger学习Koa2源码第一篇

之所以在这三个地方打断点是对应前面提到的执行了Koa2的三个api.通过这三个断点我们一步步去了解Koa2内部是怎么执行的。

最开始肯定是执行constructor();部分注释见上述截图。

this.middleware = []; 这个是用来存放通过app.use()注册的中间件的。 复制代码

重点接下来看app.use()。


通过debugger学习Koa2源码第一篇

很明显fn指的就是:

async (ctx, next) => { await next() ctx.response.status = 200 ctx.response.body = 'hello world' } 复制代码

在use(fn)方法中主要做了以下事情:

1. 错误校验, fn必须是函数, 否则给出错误提示 2. fn不推荐使用生成器函数, v2版本Koa2会进行转化, 但是v3就会不支持生成器函数了, 这里主要是对koa1的向下兼容。 3. 存储注册的中间件 3. return this. 支持链式调用 复制代码

这个时候你可以看懂this大概有这些属性:

Application { _events:Object {} _eventsCount:0 _maxListeners:undefined context:Object {} env:"development" middleware:Array(1) [] proxy:false request:Object {} response:Object {} subdomainOffset:2 Symbol(util.inspect.custom):inspect() { … } __proto__:EventEmitter } 复制代码

然后进入listen(), 这里有一段this.callback(), 我们需要去这个方法下打断点看执行了什么。

// 其实就是http.createServer(app.callback()).listen(...)的语法糖 listen(...args) { debug('listen'); const server = http.createServer(this.callback()); return server.listen(...args); } 复制代码
通过debugger学习Koa2源码第一篇
// callback()做了以下几件事: 1. 通过compose合并中间件 2. 为应用注册error事件的监听器 3. 返回一个请求处理函数handleRequest 复制代码

接下来我们看看this.createContext()和this.handleRequest(),分别打断点看代码。

note: 提一个小问题, node应该经常会发生端口占用问题。 复制代码

每次请求都会创建一个上下文对象。


通过debugger学习Koa2源码第一篇
handleRequest(ctx, fnMiddleware) { const res = ctx.res; res.statusCode = 404; // 错误处理 const onerror = err => ctx.onerror(err); const handleResponse = () => respond(ctx); // 通过第三方库on-finished监听http response,当请求结束时执行回调,这里传入的回调是context.onerror(err),即当错误发生时才执行。 onFinished(res, onerror); // 即将所有中间件执行(传入请求上下文对象ctx),之后执行响应处理函数(app.respond(ctx)),当抛出异常时同样使用cintext,onerror(err)处理。 return fnMiddleware(ctx).then(handleResponse).catch(onerror); } 复制代码

对respond打断点

/** * Response helper. * 在所有中间件执行完之后执行 */ function respond(ctx) { // allow bypassing koa // 通过设置ctx.respond = false来跳过这个函数,但不推荐这样子 if (false === ctx.respond) return; const res = ctx.res; // 上下文对象不可写时也会退出该函数 if (!ctx.writable) return; let body = ctx.body; const code = ctx.status; // ignore body // 当返回的状态码表示没有响应主体时,将响应主体置空: if (statuses.empty[code]) { // strip headers ctx.body = null; return res.end(); } // 当请求方法为HEAD时,判断响应头是否发送以及响应主体是否为JSON格式,若满足则设置响应Content-Length: if ('HEAD' == ctx.method) { if (!res.headersSent && isJSON(body)) { ctx.length = Buffer.byteLength(JSON.stringify(body)); } return res.end(); } // status body // 当返回的状态码表示有响应主体,但响应主体为空时,将响应主体设置为响应信息或状态码。并当响应头未发送时设置Content-Type与Content-Length: if (null == body) { if (ctx.req.httpVersionMajor >= 2) { body = String(code); } else { body = ctx.message || String(code); } if (!res.headersSent) { ctx.type = 'text'; ctx.length = Buffer.byteLength(body); } return res.end(body); } // 对不同的响应主体进行处理 // responses if (Buffer.isBuffer(body)) return res.end(body); if ('string' == typeof body) return res.end(body); if (body instanceof Stream) return body.pipe(res); // body: json body = JSON.stringify(body); if (!res.headersSent) { ctx.length = Buffer.byteLength(body); } res.end(body); } 复制代码

错误处理

onerror(err) { // 当err不为Error类型时抛出异常。 if (!(err instanceof Error)) throw new TypeError(util.format('non-error thrown: %j', err)); // 当 err.status 是 404 或 err.expose 是 true 时默认错误处理程序也不会输出错误 if (404 == err.status || err.expose) return; // 默认情况下,将所有错误输出到 stderr,除非 app.silent 为 true if (this.silent) return; const msg = err.stack || err.toString(); console.error(); console.error(msg.replace(/^/gm, ' ')); console.error(); } 复制代码

本文前端(javascript)相关术语:javascript是什么意思 javascript下载 javascript权威指南 javascript基础教程 javascript 正则表达式 javascript设计模式 javascript高级程序设计 精通javascript javascript教程

代码区博客精选文章
分页:12
转载请注明
本文标题:通过debugger学习Koa2源码第一篇
本站链接:https://www.codesec.net/view/627947.html


1.凡CodeSecTeam转载的文章,均出自其它媒体或其他官网介绍,目的在于传递更多的信息,并不代表本站赞同其观点和其真实性负责;
2.转载的文章仅代表原创作者观点,与本站无关。其原创性以及文中陈述文字和内容未经本站证实,本站对该文以及其中全部或者部分内容、文字的真实性、完整性、及时性,不作出任何保证或承若;
3.如本站转载稿涉及版权等问题,请作者及时联系本站,我们会及时处理。
登录后可拥有收藏文章、关注作者等权限...
技术大类 技术大类 | 前端(javascript) | 评论(0) | 阅读(32)