Koa step by step to read the source code, the overall structure analysis

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 koathis 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_modulesfind the folder koamodule, meow first glances README.mdfile, which describes koasome of the installation, use, plug-ins, and so, here we skip and go to package.jsonthe following figure

See package.jsoninside "main": "lib/application.js"Yes, this is our entrance, in lib folder below, we see that there are application.js, context.js, requrest.jsand response.js. After I modified the following simplified uncommented application.jsonly 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 Applicationthe object, there is constructora 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.requestthis.responsethis.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整个流程,还有很多细节都没有一一介绍到,比如、创建contextrequestresponse对象是怎么样子的呀?中间件是如何集中层层深入处理然后返回的呀?等等这些细节都会在下一篇会讲到(最近公司业务非常忙,不知道到猴年马月)。

写的不好的地方,让大家贱笑了。

然后最后安利一波博客,喜欢的小哥哥小姐姐可以star 哟

websit: https://github.com/naihe138/naice-blog

Guess you like

Origin www.cnblogs.com/baimeishaoxia/p/11921288.html