博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
Koa源码分析
阅读量:7070 次
发布时间:2019-06-28

本文共 5398 字,大约阅读时间需要 17 分钟。

一.下载koa源代码

先看看这个极简的启动代码:

const Koa = require('koa');const app = new Koa();// responseapp.use(ctx => {  ctx.body = 'Hello Koa';});app.listen(3000);复制代码

我们在koa源码文件夹下创建index.js文件, 将上面的代码写入,并将require('koa') 换成 require('.')

const Koa = require('.')debuggerconst app = new Koa();app.use(ctx => {  ctx.body = 'Hello Koa';});app.listen(3000);复制代码

然后进入目录运行node --inspect-brk index.js

在chrome浏览器打开调试 chrome://inspect

   1.引入Koa

  • require('.')经过路径分析

require 会依照这个顺序去查找需要的文件

Module._extensions={    .js:funciton(module,filename),    .json:funciton(module,filename),    .node:funciton(module,filename)}复制代码

通过读取package.json中的main字段得到完整路径

  • 路径分析之后,是文件定位

查找到路径之后通过 fs.readFileSync加载模块

  • 编译执行

读取文件后开始编译,首先将读取的代码script,进行组装,

即头部添加(function (exports, require, module, __filename, __dirname) { ',

尾部添加

'\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变量,却能使用的原因

2. 创建Koa对象

Application

constructor()
首先
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()
通过该方法引入中间件,中间件可以是一个普通的函数, 可以是一个generator, 可以是async函数,如果是generator那么koa会帮你转成基于promise的函数。根据use使用的先后顺序, middleware数组中会存储所有的中间件。
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()
listen函数表面上看是用于监听某个端口,实际上包裹了真正的nodejs提供的http server, createServer函数接受一个处理请求的函数,由callback()返回
listen(...args) {    debug('listen');    const server = http.createServer(this.callback());    return server.listen(...args);  }复制代码
callback()
callback函数是Application中真正核心的处理逻辑, 其返回了一个请求处理器,封装了koa所有的方法逻辑
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;  }复制代码
createContext()
这个函数与其说是创建Context对象,不如说是将koa的各个内部对象连接在一起。并设置cookies和accepts.
// 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;  }复制代码
respond()
当我们完成处理需要返回时我们设置
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);}复制代码

Context

Context是整个请求的上下文,其最特殊的地方其实是整合response和request,让你在应用的任何地方通过context获得应用相关的任何信息

Response

响应体相关的方法属性

转载地址:http://qhhll.baihongyu.com/

你可能感兴趣的文章
《Programming in Lua 3》读书笔记(二十七)
查看>>
排序--冒泡排序
查看>>
Bat 修改 xml 文件标签值
查看>>
Flask开发系列之Flask+redis实现IP代理池
查看>>
pywin32模块安装
查看>>
win32之bitmap篇
查看>>
Android SharedPreference中的几种模式说明
查看>>
Spark2 jar包运行完成,退出spark,释放资源
查看>>
poj 2823 Sliding Window (单调队列)
查看>>
poj 3411 Paid Roads (dfs)
查看>>
java实现向有序数组中插入一个元素
查看>>
新闻发布项目——访问温馨提示
查看>>
数学-基本积分公式
查看>>
js面向对象开发互联网机顶盒应用头端之五
查看>>
手机web不同屏幕字体大小高度自适应
查看>>
Machine Learning 文章导读
查看>>
JQ实现表格内数据内容的相加
查看>>
选择排序
查看>>
Linux下FTP命令的使用方法
查看>>
jQuery分页插件
查看>>