本笔记共四篇
Koa源码阅读笔记(1) -- co
Koa源码阅读笔记(2) -- compose
Koa源码阅读笔记(3) -- 服务器の启动与请求处理
Koa源码阅读笔记(4) -- ctx对象javascript
自从写了个Koa的脚手架koa2-easy,愈发以为Koa的精妙。
因而抱着知其然也要知其因此然的想法,开始阅读Koa的源代码。前端
读Koa源代码时,天然是带着诸多问题的。不管是上一篇所写的generator
函数如何自动执行,仍是对于Koa中间件如何加载,next参数如何来的。都充满了好奇。
今天写文章,并非介绍整个koa-compose
如何如何(涉及太宽,准备放在下面几篇统一介绍)。而是从自身需求出发,找到问题的答案。
而问题就是Koa中间件的加载,和next参数的来源。java
首先的是Koa加载初始化时的函数(删除部分):git
// Koa类 function Application() { this.middleware = []; } // Koa原型 var app = Application.prototype; // Koa中间件加载函数 app.use = function(fn){ if (!this.experimental) { // es7 async functions are not allowed, // so we have to make sure that `fn` is a generator function assert(fn && 'GeneratorFunction' == fn.constructor.name, 'app.use() requires a generator function'); } this.middleware.push(fn); return this; };
在这儿不难看出,Koa对象内部有个中间件的数组,其中全部中间件都会存在其中。
而在服务器启动时,则会调用并处理该数组。
源代码以下:github
var co = require('co'); var compose = require('koa-compose'); var fn = co.wrap(compose(this.middleware))
在fn被处理完后,每当有新请求,便会调用fn,去处理请求。
而在这里,co.wrap的做用是返回一个Promise
函数,用于后续自动执行generator
函数。segmentfault
因而不难看出,中间件这儿的重点,是compose函数。
而compose函数的源代码虽然很简洁,可是也很烧脑。(对我而言)api
/** * Compose `middleware` returning * a fully valid middleware comprised * of all those which are passed. * * @param {Array} middleware * @return {Function} * @api public */ // 传入中间件做为参数 function compose(middleware){ return function *(next){ // next不存在时,调用一个空的generator函数 if (!next) next = noop(); var i = middleware.length; // 倒序处理中间件,给每一个中间件传入next参数 // 而next则是下一个中间件 while (i--) { next = middleware[i].call(this, next); } return yield *next; } } function *noop(){}
在这里,得提一提Koa
中间件的调用方式。数组
app.use(function * (next) { this.set('Koa', 'Example'); yield next; }) app.use(function * (next) { this.body = 'Hello World' })
在中间件中的next,则是在koa-compose
中传入的。
而这儿, yield next
和 yield *next
也是有区别的。yield next
, next 会做为next()的value返回。
而yield *next
则是在generator
函数内执行这个generator
函数。服务器
这两天一直在读Koa的源代码,细细看来不是很难,可是被做者的奇思妙想给打动了。
接下来会继续写一些阅读笔记,由于看Koa的源代码确实是获益匪浅。app
前端路漫漫,且行且歌
最后附上本人博客地址和原文连接,但愿能与各位多多交流。