Reading good source framework has many advantages, from the perspective of the power of God to understand the design of the framework. Large architectural design, small advisable named style, and design patterns, data structures and algorithms to use certain functions and so on.
Use koa
In fact, reading the source code of a framework for when we need to go with this framework, because we used to know, is how to use an API, where there is a pit, where exquisite design.
Here we look at a simple use koa
this framework, the following code
const Koa = require('koa')
const app = new Koa()
app.use(async (ctx, next) => {
ctx.body = 'Hello World'
})
app.listen(4002)
operation result
watt? ? This service involves a request to return data from the response to these lines of code? ? Yes, you read that right, it is just a few lines of code to set up a server.
Here we look at a closer look.
Read Source
Go to node_modules
find the folder koa
module, meow first glances README.md
file, which describes koa
some of the installation, use, plug-ins, and so, here we skip and go to package.json
the following figure
See package.json
inside "main": "lib/application.js"
Yes, this is our entrance, in lib folder below, we see that there are application.js
, context.js
, requrest.js
and response.js
. After I modified the following simplified uncommented application.js
only 68 lines of code. Reading it can be very simple. As shown below:
The first step is we rely mainly on introducing a variety of
// 引入有很多 我只挑我阅读主要框架的代码模块
const response = require('./response'); // 处理response对象
const compose = require('koa-compose'); // 合并处理中间件函数
const context = require('./context'); // 整合处理context对象
const request = require('./request'); // 整合处理request对象
const http = require('http'); // node的 http原生模块
These are our major dependence
In Application
the object, there is constructor
a function, this is mainly 初始化Application对象,生成context对象、request对象、response对象
,
module.exports = class Application extends Emitter {
// 初始化 Application
constructor() {
super(); // 继承Emitter
this.middleware = []; // 初始化middleware为空数组
this.context = Object.create(context); // 生成context对象
this.request = Object.create(request); // 生成request对象
this.response = Object.create(response); // 生成response对象
}
}
阅读源码,我们先不要去扣细节,比如说Object.create(context)
生产的对象是什么?this.request
对象下面又有什么东西???,我们现在主要知道的是、this.context
是能获取或者设置请求和响应的信息的一个对象,。this.request
是请求的对象、里面可以设置或者获取请求信息的一个对象、this.response
是响应请求对象、里面可以设置或者获取响应参数和值的一个对象。大概先了解就可以了。继续往下看。
在上面运用的时候,用到了app.use(fn)
和app.listen(4002)
我们看看,源码里面试这样子的
module.exports = class Application extends Emitter {
// 初始化 Application
constructor() {
...
}
listen(...args) {
const server = http.createServer(this.callback());
return server.listen(...args);
}
use(fn) {
this.middleware.push(fn);
return this;
}
上面的代码很简单 use
函数就是把传入的fn
推入到this.middleware
的数组中,然后返回this
,方便链式调用。
然后在listen
里面用node
原生的http
模块创建一个server
,在这里顺便说一下,原生 http创建一个服务是这样子滴
const http = http.createServer((req, res) => {
res.writeHead(200, { 'Content-Type': 'text/plain' });
res.end('okay');
});
http.listen(8000)
继续看代码 ,在创建服务的时候,参数里面调用了一个this.callback()
函数,下面我们看看这个函数究竟是怎么样子的。
module.exports = class Application extends Emitter {
// 初始化 Application
constructor() {
...
}
listen(...args) {
...
}
use(fn) {
...
}
callback() {
const fn = compose(this.middleware); // 集中处理中间件数组
const handleRequest = (req, res) => {
const ctx = this.createContext(req, res); // 整合req、res、context、request、response
return this.handleRequest(ctx, fn); // 返回handleRequest
};
return handleRequest;
}
handleRequest(ctx, fnMiddleware) {
const handleResponse = () => respond(ctx); // 最终响应函数
return fnMiddleware(ctx).then(handleResponse) // 处理完中间件,然后传到下一响应函数
}
// 创建整合新的 context.
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;
context.originalUrl = request.originalUrl = req.url;
context.cookies = new Cookies(req, res, {
keys: this.keys,
secure: request.secure
});
request.ip = request.ips[0] || req.socket.remoteAddress || '';
context.accept = request.accept = accepts(req);
context.state = {};
return context;
}
};
上面我们可以看出在callback
函数里面有一个const fn = compose(this.middleware);
这个函数就是把this.middleware
数组传进去,然后集中处理中间件,然后会返回处理完中间件的fn。
继续下一行
const handleRequest = (req, res) => {
const ctx = this.createContext(req, res);
return this.handleRequest(ctx, fn);
};
继续进入到handleRequest
函数里面的const ctx = this.createContext(req, res);
这个把原生的http的请求对象req
和响应对象res
作为参数传进去,然后在createContext
函数(看上面最大那坨代码)在里面,把this.request
、this.response
、this.context
、请求对象req
、响应对象res
都整,做各种整合、处理得到新的context
对象返回出去。
也就是强大的ctx
,得到ctx
之后,下一行返回return this.handleRequest(ctx, fn);
this.handleRequest(ctx, fn)
代码如下
handleRequest(ctx, fnMiddleware) {
const handleResponse = () => respond(ctx);
return fnMiddleware(ctx).then(handleResponse).catch(onerror);
}
这个函数 就是处理完中间件处理之后的返回的函数把ctx
传下去,最后流通到respond(ctx);
这个函数,
那么我们看看这个函数被我简化后是怎么样子的,如下
// 一些容错判断或者提示我全部删了
function respond(ctx) {
const res = ctx.res;
let body = ctx.body;
res.end(body);
}
通过ctx
拿到响应对象,和响应值、通过end
方法会通知服务器,所有响应头和响应主体都已被发送,即服务器将其视为已完成。看上面原生的http的服务方法。
最后附上一个流程图
这个只是介绍application
整个流程,还有很多细节都没有一一介绍到,比如、创建context
、request
、response
对象是怎么样子的呀?中间件是如何集中层层深入处理然后返回的呀?等等这些细节都会在下一篇会讲到(最近公司业务非常忙,不知道到猴年马月)。
写的不好的地方,让大家贱笑了。
然后最后安利一波博客,喜欢的小哥哥小姐姐可以star 哟