阅读好的框架的源码有很多好处,从大神的视角去理解整个框架的设计思想。大到架构设计,小到可取的命名风格,还有设计模式、实现某类功能使用到的数据结构和算法等等。

使用koa

其实某个框架阅读源码的时候,首先我们要会去用这个框架,因为用了我们才知道,某个API是怎么用,哪里有坑,哪里设计的精妙。

下面我们就简单用一下koa这个框架,如下代码


  1. const Koa = require('koa')
  2. const app = new Koa()
  3. app.use(async (ctx, next) => {
  4. ctx.body = 'Hello World'
  5. })
  6. app.listen(4002)

运行结果

瓦特??这个服务会涉及到从请求到响应返回数据,就这几行代码?? 是的,你没有看错,就是单单这几行代码就可以搭建了一个服务器。

下面我们看看一探究竟。

阅读源码

去到node_modules文件夹下找到koa模块,先喵几眼README.md文件,里面介绍了koa的一些安装、用法、插件等等,这里我们跳过,然后转到package.json如下图

看到package.json里面的"main": "lib/application.js"没错,这就是我们的入口,在lib文件夹下面,我们看到里面有application.jscontext.jsrequrest.jsresponse.js。下面经过我修改简化去掉注释application.js就只有68行代码。阅读起来可以说是非常简单了。如下图:

第一步是我们引入各种主要依赖

  1. // 引入有很多 我只挑我阅读主要框架的代码模块
  2. const response = require('./response'); // 处理response对象
  3. const compose = require('koa-compose'); // 合并处理中间件函数
  4. const context = require('./context'); // 整合处理context对象
  5. const request = require('./request'); // 整合处理request对象
  6. const http = require('http'); // node的 http原生模块

以上就是我们的主要依赖

Application的对象中,有constructor函数,这个主要是初始化Application对象,生成context对象、request对象、response对象

  1. module.exports = class Application extends Emitter {
  2. // 初始化 Application
  3. constructor() {
  4. super(); // 继承Emitter
  5. this.middleware = []; // 初始化middleware为空数组
  6. this.context = Object.create(context); // 生成context对象
  7. this.request = Object.create(request); // 生成request对象
  8. this.response = Object.create(response); // 生成response对象
  9. }
  10. }

阅读源码,我们先不要去扣细节,比如说Object.create(context)生产的对象是什么?this.request对象下面又有什么东西???,我们现在主要知道的是、this.context是能获取或者设置请求和响应的信息的一个对象,。this.request是请求的对象、里面可以设置或者获取请求信息的一个对象、this.response是响应请求对象、里面可以设置或者获取响应参数和值的一个对象。大概先了解就可以了。继续往下看。

在上面运用的时候,用到了app.use(fn)app.listen(4002) 我们看看,源码里面试这样子的


  1. module.exports = class Application extends Emitter {
  2. // 初始化 Application
  3. constructor() {
  4. ...
  5. }
  6. listen(...args) {
  7. const server = http.createServer(this.callback());
  8. return server.listen(...args);
  9. }
  10. use(fn) {
  11. this.middleware.push(fn);
  12. return this;
  13. }

上面的代码很简单 use函数就是把传入的fn 推入到this.middleware的数组中,然后返回this,方便链式调用。

然后在listen里面用node原生的http模块创建一个server,在这里顺便说一下,原生 http创建一个服务是这样子滴

  1. const http = http.createServer((req, res) => {
  2. res.writeHead(200, { 'Content-Type': 'text/plain' });
  3. res.end('okay');
  4. });
  5. http.listen(8000)

继续看代码 ,在创建服务的时候,参数里面调用了一个this.callback()函数,下面我们看看这个函数究竟是怎么样子的。


  1. module.exports = class Application extends Emitter {
  2. // 初始化 Application
  3. constructor() {
  4. ...
  5. }
  6. listen(...args) {
  7. ...
  8. }
  9. use(fn) {
  10. ...
  11. }
  12. callback() {
  13. const fn = compose(this.middleware); // 集中处理中间件数组
  14. const handleRequest = (req, res) => {
  15. const ctx = this.createContext(req, res); // 整合req、res、context、request、response
  16. return this.handleRequest(ctx, fn); // 返回handleRequest
  17. };
  18. return handleRequest;
  19. }
  20. handleRequest(ctx, fnMiddleware) {
  21. const handleResponse = () => respond(ctx); // 最终响应函数
  22. return fnMiddleware(ctx).then(handleResponse) // 处理完中间件,然后传到下一响应函数
  23. }
  24. // 创建整合新的 context.
  25. createContext(req, res) {
  26. const context = Object.create(this.context);
  27. const request = context.request = Object.create(this.request);
  28. const response = context.response = Object.create(this.response);
  29. context.app = request.app = response.app = this;
  30. context.req = request.req = response.req = req;
  31. context.res = request.res = response.res = res;
  32. request.ctx = response.ctx = context;
  33. request.response = response;
  34. response.request = request;
  35. context.originalUrl = request.originalUrl = req.url;
  36. context.cookies = new Cookies(req, res, {
  37. keys: this.keys,
  38. secure: request.secure
  39. });
  40. request.ip = request.ips[0] || req.socket.remoteAddress || '';
  41. context.accept = request.accept = accepts(req);
  42. context.state = {};
  43. return context;
  44. }
  45. };

上面我们可以看出在callback函数里面有一个const fn = compose(this.middleware); 这个函数就是把this.middleware数组传进去,然后集中处理中间件,然后会返回处理完中间件的fn。

继续下一行

  1. const handleRequest = (req, res) => {
  2. const ctx = this.createContext(req, res);
  3. return this.handleRequest(ctx, fn);
  4. };

继续进入到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)代码如下


  1. handleRequest(ctx, fnMiddleware) {
  2. const handleResponse = () => respond(ctx);
  3. return fnMiddleware(ctx).then(handleResponse).catch(onerror);
  4. }

这个函数 就是处理完中间件处理之后的返回的函数把ctx传下去,最后流通到respond(ctx);这个函数,

那么我们看看这个函数被我简化后是怎么样子的,如下

  1. // 一些容错判断或者提示我全部删了
  2. function respond(ctx) {
  3. const res = ctx.res;
  4. let body = ctx.body;
  5. res.end(body);
  6. }

通过ctx拿到响应对象,和响应值、通过end方法会通知服务器,所有响应头和响应主体都已被发送,即服务器将其视为已完成。看上面原生的http的服务方法。

最后附上一个流程图

这个只是介绍application整个流程,还有很多细节都没有一一介绍到,比如、创建contextrequestresponse对象是怎么样子的呀?中间件是如何集中层层深入处理然后返回的呀?等等这些细节都会在下一篇会讲到(最近公司业务非常忙,不知道到猴年马月)。

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

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

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

一步步去阅读koa源码,整体架构分析的更多相关文章

  1. jquery源码 整体架构

    一.对外提供接口 对外提供了jQuery. //可以通过jQuery或者$来找到jQuery (function(window,undefined){ //(21,94) 定义了一些变量和函数 jQu ...

  2. leveldb 源码--总体架构分析

    一 本文目的 对leveldb的总体设计框架分析(关于leveldb基本原理,此文不做阐述,读者可以自行检索文章阅读即可),对leveldb中底层数据存储数据格式,内存数据模型,compact,版本管 ...

  3. 阅读nsq源码 ---初步架构设计图

     

  4. Koa源码解析,带你实现一个迷你版的Koa

    前言 本文是我在阅读 Koa 源码后,并实现迷你版 Koa 的过程.如果你使用过 Koa 但不知道内部的原理,我想这篇文章应该能够帮助到你,实现一个迷你版的 Koa 不会很难. 本文会循序渐进的解析内 ...

  5. koa源码阅读[3]-koa-send与它的衍生(static)

    koa源码阅读的第四篇,涉及到向接口请求方提供文件数据. 第一篇:koa源码阅读-0第二篇:koa源码阅读-1-koa与koa-compose第三篇:koa源码阅读-2-koa-router 处理静态 ...

  6. koa源码阅读[2]-koa-router

    koa源码阅读[2]-koa-router 第三篇,有关koa生态中比较重要的一个中间件:koa-router 第一篇:koa源码阅读-0第二篇:koa源码阅读-1-koa与koa-compose k ...

  7. koa源码阅读[0]

    koa源码阅读[0] Node.js也是写了两三年的时间了,刚开始学习Node的时候,hello world就是创建一个HttpServer,后来在工作中也是经历过Express.Koa1.x.Koa ...

  8. 如何阅读Java源码?

    阅读本文大概需要 3.6 分钟. 阅读Java源码的前提条件: 1.技术基础 在阅读源码之前,我们要有一定程度的技术基础的支持. 假如你从来都没有学过Java,也没有其它编程语言的基础,上来就啃< ...

  9. 阅读 Android源码的一些姿势

    日常开发中怎么阅读源码 找到正确的源码 IDE 是日常经常用的东西,Eclipse 就不说了,直接从 Android Studio(基于 IntelliJ Community 版本改造)开始. 我们平 ...

随机推荐

  1. [LC] 207. Course Schedule

    There are a total of n courses you have to take, labeled from 0 to n-1. Some courses may have prereq ...

  2. 吴裕雄--天生自然 JAVA开发学习:序列化

    public final void writeObject(Object x) throws IOException public final Object readObject() throws I ...

  3. 让Spring不再难懂-mvc篇

    spring mvc简介与运行原理 Spring的模型-视图-控制器(MVC)框架是围绕一个DispatcherServlet来设计的,这个Servlet会把请求分发给各个处理器,并支持可配置的处理器 ...

  4. Oracle中的 timestamp 和 timestamp with time zone, timestamp with local time zone

    SQL> select dbtimezone, sessiontimezone from dual; DBTIME ------ SESSIONTIMEZONE ---------------- ...

  5. Exchange Onine功能介绍

    Exchange Online是Office 365中提供的一个邮箱服务.Microsoft Exchange Online是将Microsoft Exchange Server功能作为基于云的服务提 ...

  6. 学会使用数据讲故事——Excel研究网络研讨会

    编者按:在数据密集型研究的新时代,Excel将成为研究者讲故事的强大工具.在即将举行的Excel研究网络研讨会中,我们将与你探讨如何用新的方式来寻找.查询.分析数据并实现数据可视化.Office 36 ...

  7. deeplearning.ai 序列模型 Week 1 RNN(Recurrent Neural Network)

    1. Notations 循环序列模型的输入和输出都是时间序列.$x^{(i)<t>}$表示第$i$个输入样本的第$t$个元素,$T_x^{(i)}$表示输入的第$i$个样本的元素个数:$ ...

  8. springboot项目实现jar包外配置文件管理

    背景 为实现快速搭建和开发,项目以Springboot框架搭建,springboot搭建的项目可以将项目直接打成jar包并运行,无需自己安装配置Tomcat或者其他服务器,是一种方便快捷的部署方式. ...

  9. JavaScript学习总结(五)原型和原型链详解

    转自:http://segmentfault.com/a/1190000000662547 私有变量和函数 在函数内部定义的变量和函数,如果不对外提供接口,外部是无法访问到的,也就是该函数的私有的变量 ...

  10. Flash Builder 使用

    1. 解决编译慢的问题:用记事本打开安装目录下的 FlashBuilder.ini ,将里面设置的容量都扩大一半,如-Xms256m改为 -Xms512m,另外几项类似修改: 定位到相应版本的sdks ...