深入理解 Koa 框架中间件原理


Posted in Javascript onOctober 18, 2018

Node 主要用在开发 Web 应用,koa 是目前 node 里最流行的 web 框架。

在 Node 开启一个 http 服务简直易如反掌,官网 demo。

const http = require("http");

const server = http.createServer((req, res) => {
 res.statusCode = 200;
 res.setHeader("Content-Type", "text/plain");
 res.end("Hello World\n");
});

const hostname = "127.0.0.1";
const port = 3000;
server.listen(port, hostname, () => {
 console.log(`Server running at http://${hostname}:${port}/`);
});
  • 引入 http 模块, http 的 createServer 方法创建了一个 http.Server 的实例。
  • server 监听 3000 端口。
  • 我们传入到 createServer 里的函数实际是监听 request 事件的回调,每当请求进来,监听函数就会执行。
  • request 事件的监听函数,其函数接受两个参数,分别是 req 和 res 。其中 req 是一个可读流, res 是一个可写流。我们通过 req 获取 http 请求的所有信息,同时将数据写入到 res 来对该请求作出响应。

koa 应用

koa 如何创建一个 server, 直接上个官网的例子

const Koa = require("koa");
const app = new Koa();

// x-response-time

app.use(async (ctx, next) => {
 const start = Date.now();
 await next();
 const ms = Date.now() - start;
 ctx.set("X-Response-Time", `${ms}ms`);
});

// logger

app.use(async (ctx, next) => {
 const start = Date.now();
 await next();
 const ms = Date.now() - start;
 console.log(`${ctx.method} ${ctx.url} - ${ms}`);
});

// response

app.use(async ctx => {
 ctx.body = "Hello World";
});

app.listen(3000);

中间件概念在编程中使用广泛, 不管是前端还是后端, 在实际编程中或者框架设计都有使用到这种实用的模型。

基本上,Koa 所有的功能都是通过中间件实现的。

每个中间件默认接受两个参数,第一个参数是 Context 对象,第二个参数是 next 函数。只要调用 next 函数,就可以把执行权转交给下一个中间件。

如果中间件内部没有调用 next 函数,那么执行权就不会传递下去。

多个中间件会形成一个栈结构(middle stack),以“先进后出”(first-in-last-out)的顺序执行。整个过程就像,先是入栈,然后出栈的操作。

上面代码的执行顺序是:

请求 ==> x-response-time 中间件 ==> logger 中间件 ==> response中间件 ==> logger 中间件 ==> response-time 中间件 ==> 响应

理解 Koa 的中间件机制(源码分析)

阅读源码,化繁为简,我们看看 koa 的中间件系统是如何实现的。

class Application extends Emitter {
 constructor() {
  super();
  this.middleware = [];
 },

 use(fn) {
  this.middleware.push(fn);
  return this;
 },

 callback() {
  const fn = compose(this.middleware);

  return function(req, res) {
   return fn(ctx);
  };
 },

 listen(...args) {
  const server = http.createServer(this.callback());
  return server.listen(...args);
 }
}

好了,精简结束,一不小心,去枝末节,最后只剩下不到 20 行代码。

这就是框架的核心,简化后的代码非常清晰,有点不可思议,但核心就是这么简单。

我们先分析以上代码做了什么事。

  • 我们定义了一个 middleware 数组来存储中间件。
  • 我们定一个了一个 use 方法来注册一个中间件。其实就是简单的 push 到自身的 mideware 这个数组中。
  • 我们还使用了一个 compose 方法,传入 middleware ,应该是做了一些处理,返回了一个可执行的方法。

你一定对中间的 compose 方法很好奇,初此之外的代码都容易理解,唯独这个 compose 不太知道究竟做了什么。

其实, compose 就是整个中间件框架的核心。

compose 之外,代码已经很清楚的定义了

  • 中间件的存储
  • 中间件的注册

而 compose 方法做了最为重要的一件事

  • 中间件的执行

核心源码 compose

先上码

function compose(middleware) {
 return function(context, next) {
  // last called middleware #
  let index = -1;
  return dispatch(0);
  function dispatch(i) {
   if (i <= index)
    return Promise.reject(new Error("next() called multiple times"));
   index = i;
   let fn = middleware[i];
   if (i === middleware.length) fn = next;
   if (!fn) return Promise.resolve();
   try {
    return Promise.resolve(fn(context, dispatch.bind(null, i + 1)));
   } catch (err) {
    return Promise.reject(err);
   }
  }
 };
}

我试图去简化一下这个方法,但方法本身已经足够简洁。

代码很简洁。

通过 next()传递 实现中间件调用, 结合 Promise 采用 递归调用 的通知机制。

看图

深入理解 Koa 框架中间件原理

这种形式的控制流让整个 Koa 框架中间件的访问呈现出 自上而下的中间件流 + 自下而上的 response 数据流 的形式。

Koa 本身做的工作仅仅是定制了中间件的编写规范,而不内置任何中间件。一个 web request 会通过 Koa 的中间件栈,来动态完成 response 的处理。

koa 在中间件语法上面采用了 async + await 语法来生成 Promise 形式的程序控制流。

总结

koa 是非常精简的框架, 其中的精粹思想就是洋葱模型(中间件模型), koa 框架的中间件模型非常好用并且简洁, 但是也有自身的缺陷, 一旦中间件数组过于庞大, 性能会有所下降,我们需要结合自身的情况与业务场景作出最合适的选择.

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持三水点靠木。

Javascript 相关文章推荐
页面加载完毕后滚动条自动滚动一定位置
Feb 20 Javascript
jQuery标签替换函数replaceWith()的使用例子
Aug 28 Javascript
jQuery实现倒计时按钮功能代码分享
Sep 03 Javascript
Javascript学习笔记之函数篇(五) : 构造函数
Nov 23 Javascript
JS实时弹出新消息提示框并有提示音响起的实现代码
Apr 20 Javascript
jquery实现无刷新验证码的简单实例
May 19 Javascript
静态页面html中跳转传值的JS处理技巧
Jun 22 Javascript
js装饰设计模式学习心得
Feb 17 Javascript
解决vue 更改计算属性后select选中值不更改的问题
Mar 02 Javascript
详解在vue-cli中使用graphql即vue-apollo的用法
Sep 08 Javascript
基于javascript处理nginx请求过程详解
Jul 07 Javascript
canvas实现贪食蛇的实践
Feb 15 Javascript
JS实现十分钟倒计时代码实例
Oct 18 #Javascript
基于Vue实现可以拖拽的树形表格实例详解
Oct 18 #Javascript
JavaScript的词法结构精华篇
Oct 17 #Javascript
Javascript中parseInt的正确使用方式
Oct 17 #Javascript
教你如何编写Vue.js的单元测试的方法
Oct 17 #Javascript
详解vue如何使用rules对表单字段进行校验
Oct 17 #Javascript
Vue绑定内联样式问题
Oct 17 #Javascript
You might like
php中邮箱地址正则表达式实现与详解
2012/04/24 PHP
深入理解PHP之源码目录结构与功能说明
2016/06/01 PHP
php的无刷新操作实现方法分析
2020/02/28 PHP
js获取上传文件大小示例代码
2014/04/10 Javascript
基于jQuery实现搜索关键字自动匹配功能
2020/03/26 Javascript
JavaScript中最容易混淆的作用域、提升、闭包知识详解(推荐)
2016/09/05 Javascript
微信小程序 location API实例详解
2016/10/02 Javascript
H5手机端多文件上传预览插件
2017/04/21 Javascript
Vue.js实现价格计算器功能
2020/03/30 Javascript
js精确的加减乘除实例
2017/11/14 Javascript
JavaScript中关于class的调用方法
2017/11/28 Javascript
Vue2.0系列之过滤器的使用
2018/03/01 Javascript
使用 Node.js 实现图片的动态裁切及算法实例代码详解
2018/09/29 Javascript
Jquery和CSS实现选择框重置按钮功能
2018/11/08 jQuery
关于Vue Router中路由守卫的应用及在全局导航守卫中检查元字段的方法
2018/12/09 Javascript
JavaScript中十种一步拷贝数组的方法实例详解
2019/04/22 Javascript
深入理解令牌认证机制(token)
2019/08/22 Javascript
Vue状态模式实现窗口停靠功能(灵动、自由, 管理后台Admin界面)
2020/03/06 Javascript
jQuery cookie的公共方法封装和使用示例
2020/06/01 jQuery
浅谈vue.watch的触发条件是什么
2020/11/07 Javascript
Vue2.x-使用防抖以及节流的示例
2021/03/02 Vue.js
python 循环while和for in简单实例
2016/08/16 Python
python无限生成不重复(字母,数字,字符)组合的方法
2018/12/04 Python
python 发送和接收ActiveMQ消息的实例
2019/01/30 Python
Python实现二叉树前序、中序、后序及层次遍历示例代码
2019/05/18 Python
ubuntu 18.04搭建python环境(pycharm+anaconda)
2019/06/14 Python
关于sys.stdout和print的区别详解
2019/12/05 Python
Python虚拟环境库virtualenvwrapper安装及使用
2020/06/17 Python
美国办公用品购物网站:Quill.com
2016/09/01 全球购物
Elemis美国官网:英国的第一豪华护肤品牌
2018/03/15 全球购物
德国价格合理的品牌商品购物网站:averdo
2019/03/21 全球购物
人力资源管理专业自荐信
2014/06/24 职场文书
物理分数没达标检讨书
2014/09/13 职场文书
毕业生自荐材料范文
2014/12/30 职场文书
汤姆叔叔的小屋读书笔记
2015/06/30 职场文书
原生JS实现飞机大战小游戏
2021/06/09 Javascript