先看看这个极简的启动代码:
node
const Koa = require('koa');
const app = new Koa();
// response
app.use(ctx => {
ctx.body = 'Hello Koa';
});
app.listen(3000);复制代码
咱们在koa源码文件夹下建立index.js文件, 将上面的代码写入,并将require('koa')
换成 require('.')
git
const Koa = require('.')
debugger
const app = new Koa();
app.use(ctx => {
ctx.body = 'Hello Koa';
});
app.listen(3000);
复制代码
而后进入目录运行node --inspect-brk index.js
github
在chrome浏览器打开调试 chrome://inspectchrome
1.引入Koajson
require 会依照这个顺序去查找须要的文件
数组
Module._extensions={
.js:funciton(module,filename),
.json:funciton(module,filename),
.node:funciton(module,filename)
}复制代码
经过读取package.json中的main字段获得完整路径promise
查找到路径以后经过 fs.readFileSync加载模块浏览器
读取文件后开始编译,首先将读取的代码script,进行组装,bash
即头部添加(function (exports, require, module, __filename, __dirname) { ', cookie
'\n});'
NativeModule.wrap = function(script) {
return NativeModule.wrapper[0] + script + NativeModule.wrapper[1];
};
NativeModule.wrapper = [
'(function (exports, require, module, __filename, __dirname) { ',
'\n});'
];复制代码
这就是为何每一个模块没有定义exports, require, module, __filename, __dirname变量,却能使用的缘由
new Koa()
的时候就是new的这个对象, 最关键的是建立了context,request,response对象
constructor() {
super();
this.proxy = false;
// 中间件初始化为一个列表
this.middleware = [];
this.subdomainOffset = 2;
// 默认为开发环境
this.env = process.env.NODE_ENV || 'development';
// 建立Context, Request,Response对象,为何要用Object.create函数呢?
this.context = Object.create(context);
this.request = Object.create(request);
this.response = Object.create(response);
}复制代码
use(fn) {
if (isGeneratorFunction(fn)) {
deprecate('Support for generators will be removed in v3. ' +
'See the documentation for examples of how to convert old middleware ' +
'https://github.com/koajs/koa/blob/master/docs/migration.md');
fn = convert(fn);
}
this.middleware.push(fn);
return this;
}复制代码
listen(...args) {
debug('listen');
const server = http.createServer(this.callback());
return server.listen(...args);
}
复制代码
callback() {
// compose函数将注册的中间件组合成了一个函数,相似于递归
const fn = compose(this.middleware);
// Koa Application 扩展了 Emitter类, listeners 是Emitter类的属性.
// 这里表示若没有注册error事件处理函数, 则注册一个
if (!this.listeners('error').length) this.on('error', this.onerror);
// 返回的请求处理函数, req, res是createServer回调时会传入的nodejs请求和响应对象。
const handleRequest = (req, res) => {
// 默认的的状态码为404
res.statusCode = 404;
// 建立koa应用的上下文, context将不少属性和方法都代理到这个对象上方便开发.
const ctx = this.createContext(req, res);
// 使用 ctx.onerror处理请求错误, 详见Context
const onerror = err => ctx.onerror(err);
// 处理响应函数
const handleResponse = () => respond(ctx);
// 请求完成以后若是出错调用onerror
onFinished(res, onerror);
// 等中间件都处理完了以后处理响应
return fn(ctx).then(handleResponse).catch(onerror);
};
return handleRequest;
}
复制代码
// req, res 是node的原生请求响应对象,是全部信息的来源.
// request,response是koa提供的方便咱们开发使用的请求响应对象.
createContext(req, res) {
const 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 = this;
context.req = request.req = response.req = req;
context.res = request.res = response.res = res;
request.ctx = response.ctx = context;
request.response = response;
response.request = request;
// originalUrl即 req.url
context.originalUrl = request.originalUrl = req.url;
// cookies 直接使用的第三方库
context.cookies = new Cookies(req, res, {
keys: this.keys,
secure: request.secure
});
// 更经常使用的从ip中读取请求方的IP地址, ips是?
request.ip = request.ips[0] || req.socket.remoteAddress || '';
// 使用accepts设置请求能接受的内容类型
context.accept = request.accept = accepts(req);
// ?
context.state = {};
return context;
}
复制代码
this.body = 'xxx'
而后函数返回,不须要咱们手动调用res.end(),由于koa已经帮咱们封装好了.
/**
* Response helper.
*/
function respond(ctx) {
// ...
// 若是HTTP状态码表示内容应该为空,则设空返回
if (statuses.empty[code]) {
ctx.body = null;
return res.end();
}
if ('HEAD' === ctx.method) {
// 要求返回响应头,若是headersSent为false
if (!res.headersSent && isJSON(body)) {
ctx.length = Buffer.byteLength(JSON.stringify(body));
}
return res.end();
}
// 若是没有设置body,只设置了status,则用状态码或message设置body.
if (null == body) {
body = ctx.message || String(code);
if (!res.headersSent) {
ctx.type = 'text';
ctx.length = Buffer.byteLength(body);
}
return res.end(body);
}
// koa 支持Buffer, string, Stream类型的数据
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 处理普通json类型返回.
body = JSON.stringify(body);
if (!res.headersSent) {
ctx.length = Buffer.byteLength(body);
}
res.end(body);
}
复制代码