koa2源码剖析和koa-compose实现

简介

Koa由 Express 幕后的原班人马打造, 致力于成为 web 应用和 API 开发领域中的一个更小、更富有表现力、更健壮的基石。 通过利用 async 函数,Koa 帮你丢弃回调函数。同时koa的中间件机制和洋葱模型是它独有的特点,并且它没有捆绑任何中间件,而是可以让用户以更优雅的方式去扩展。

Koa是一个精简的web框架,它主要做了以下几件事:

  • 为request和response对象赋能,并基于它们封装成一个context对象,可以通过get,set调用参数
  • 基于async/await的中间件容器机制

源码解读

打开koa的源码,核心文件共四个在lib目录下:

  • application.js
  • context.js
  • request.js
  • response.js
application(初始化)

application是Koa2的入口,里面引入了request.js,response.js,context.js。然后用构造函数的形式,暴露出来。

这里列出主要结构代码:

module.exports = class Application extends Emitter {constructor(options) { // xxxthis.middleware = []; // 注:用来存放通过use函数引入的中间件的数组。this.context = Object.create(context); // 注:创建content对象this.request = Object.create(request); // 注:同上this.response = Object.create(response); // 注:同上}listen(...args) {  // 注:创建node服务实例debug('listen');const server = http.createServer(this.callback());return server.listen(...args);}use(fn) { // 注: 用来把中间件加入到middleware数组里面// xxxthis.middleware.push(fn);return this;}callback() {  // 注: 返回一个函数给node服务,作为处理请求的回调函数const fn = compose(this.middleware);  // 注: 很重要,将所有的中间件通过compose组合一下,弄成洋葱模型。这里是Koa的精髓部分if (!this.listenerCount('error')) this.on('error', this.onerror);const handleRequest = (req, res) => {const ctx = this.createContext(req, res);return this.handleRequest(ctx, fn);};return handleRequest;}handleRequest(ctx, fnMiddleware) { // 注:处理错误// xxx}createContext(req, res) { // 注:创建ctx对象,并且把req,res写入ctx// xxx }
content

里面都是一堆对参数的set get设置,方便一些参数的获取。

const util = require('util');
const createError = require('http-errors');
const httpAssert = require('http-assert');
const delegate = require('delegates');
const statuses = require('statuses');
const Cookies = require('cookies');const COOKIES = Symbol('context#cookies');const proto = module.exports = {// xxx 注:一些方法函数,toJosn之类的。
}// 注:这里就是为了在application.js 里面createContent 的时候,把一些response,request的对象属性代理给content。
delegate(proto, 'response').method('attachment').xxxdelegate(proto, 'request').method('acceptsLanguages').xxx
request
module.exports = {// 注:基于req封装很多便利的函数和属性,可以让application.js 里面request对象直接调用。get header() {return this.req.headers;},set header(val) {this.req.headers = val;},
response
module.exports = {// 注:基于res封装很多便利的函数和属性,可以让application.js 里面responset对象直接调用。get headers() {return this.header;},get status() {return this.res.statusCode;},

中间件机制koa-compose

介绍application.js的时候,提到里面有个koa-compose,这里是整个koa框架的精髓。

我们所知的koa中间件是一个洋葱模型:

在这里插入图片描述
现在我们来学习compose是怎么实现洋葱模型的。

要学会洋葱模型,我们必须先要懂一个函数式编程的东西,就是函数合成和函数柯里化。compose就是运用了函数式编程的方式,来实现洋葱模型的。

函数的合成与柯里化
  • 函数合成:

如果一个值要经过多个函数,才能变成另外一个值,就可以把所有中间步骤合并成一个函数,这叫做"函数的合成"(compose)。

const compose = function (f, g) {return function (x) {return f(g(x));};
}
  • 柯理化:

f(x)和g(x)合成为f(g(x)),有一个隐藏的前提,就是f和g都只能接受一个参数。如果可以接受多个参数,比如f(x, y)和g(a, b, c),函数合成就非常麻烦。

这时就需要函数柯里化了。所谓"柯里化",就是把一个多参数的函数,转化为单参数函数。


// 柯里化之前
function add(x, y) {return x + y;
}add(1, 2) // 3// 柯里化之后
function addX(y) {return function (x) {return x + y;};
}addX(2)(1) // 3
compose实现

学习完函数式编程之后,我们来实现compose函数。

// compose.jsfunction compose(middlewares) {return function (ctx) {return dispatch(0)function dispatch(i) {let fn = middlewares[i] // 注:application.js 里面用来存放中间件的数组if (!fn) { return Promise.resolve()}return Promise.resolve(fn(ctx,function next() {return dispatch(i + 1)}))}x}
}// 写几个中间件案例:async function fn1(ctx,next) {console.log("fn1"); await next(); console.log("end fn1");
}async function fn2(ctx,next) {console.log("fn2");await next();console.log("end fn2");
}function fn3(ctx,next) {console.log("fn3");
}const middlewares = [fn1, fn2, fn3];const finalFn = compose(middlewares);finalFn('ctx'); // 注:这里ctx随便用个字符串代替,正式调用的话,ctx是createContent创建的上下文对象,里面有response,request等等。

在这里插入图片描述
洋葱模型,先从外往里面执行,然后再从里往外执行。

  • ① 先执行fn1,打印fn1
  • ② 遇到next函数,fn1后面代码挂起,等待next1执行完毕(这里命名next1,方便理解)
  • ③ next1通过dispatch函数执行fn2
  • ④ 打印fn2
  • ⑤ 遇到next函数,fn2后面代码挂起,等待next2执行完毕
  • ⑥ next2通过dispatch函数执行fn3
  • ⑦ 打印fn3
  • ⑧ 此时fn3函数全部走完,通知fn2,next2已经执行完毕,运行fn2后面代码,打印end fn2
  • ⑨ 此时fn2函数也已经全部走完,再通知fn1,next1已经执行完,运行fn1后面的代码,打印end fn1

到这里一个全部compose就完成了,简单来说这里就是一个Promise嵌套,加上函数式编程。

想看实现的koa2项目代码(application,response,request,compose)可以去我的githubk-koa 看完整项目。


本文来自互联网用户投稿,文章观点仅代表作者本人,不代表本站立场,不承担相关法律责任。如若转载,请注明出处。 如若内容造成侵权/违法违规/事实不符,请点击【内容举报】进行投诉反馈!

相关文章

立即
投稿

微信公众账号

微信扫一扫加关注

返回
顶部