
koa框架需要实现四个大模块,分别是:
- 封装node http server、创建Koa类构造函数
- 构造request、response、context对象
- 中间件机制和剥洋葱模型的实现
- 错误捕获和错误处理
运行脚本 app.js, 访问相应的端口, 控制台打印1,2,3,4,5
const Koa = require("koa");const app = new Koa();app.use(async (ctx, next) => {console.log(1);await next();console.log(5);});app.use(async (ctx, next) => {console.log(2);await next();console.log(4);});app.use(async (ctx) => {console.log(3);ctx.body = "Hello World";});app.listen(3000);
1. 封装node http server、创建Koa类构造函数
./src/application.js
const Emitter = require('events');const http = require('http');module.exports = class Application extends Emitter {constructor(options) {super();this.callbackFn;}listen(...args) {const server = http.createServer(this.callback());return server.listen(...args);}use(fn) {this.callbackFn = fn;return this;}callback() {return (req, res) => {this.callbackFn(req, res);}}}
运行脚本app.js
const Koa = require("./src/application.js");const app = new Koa();app.use((req, res) => {// res.writeHead(200);res.end('hello world');});app.listen(3000);
中间件机制和剥洋葱模型的实现

./src/application.js
const Emitter = require('events');const http = require('http');const compose = require('./compose.js')module.exports = class Application extends Emitter {constructor(options) {super();this.middleware = [];this.compose = compose;}listen(...args) {const server = http.createServer(this.callback());return server.listen(...args);}use(fn) {this.middleware.push(fn);return this;}responseBody (ctx) {}onError (err, ctx) {}callback() {return (req, res) => {const ctx = "";let respond = () => this.responseBody(ctx);let onError = (err) => this.onError(err, ctx);let fn = this.compose();return fn(ctx).then(respond).catch(onError)}}}
./src/compose.js
module.exports = composefunction compose (middleware) {return async ctx => {function createNext(middleware, oldNext) {return async() => {await middleware(ctx, oldNext);}}let len = this.middleware.length;let next = async() => {return Promise.resolve();}for (let i = len - 1; i >= 0; i--) {let currentMiddleware = this.middleware[i];next = createNext(currentMiddleware, next);}await next();}}
运行脚本./app.js, 访问依次打印1,2,3, 4,5
构造request、response、context对象
./src/request.js
let url = require('url');module.exports = {get query() {return url.parse(this.req.url, true).query;}}
./src/response.js
module.exports = {get body () {return this._body;},set body (data) {this._body = data;},get status () {return this.res.statusCode;},set status (statusCode) {this.res.statusCode = statusCode;}}
./src/context.js
let proto = {};function delegateSet(property, name) {proto.__defineSetter__(name, function(val) {this[property][name] = val;})}function delegateGet (property, name) {proto.__defineGetter__(name, function() {return this[property][name];})}let requestSet = [];let requestGet = ['query'];let responseSet = ['body', 'status'];let responseGet = responseSet;requestSet.forEach(ele => {delegateSet('request', ele);})requestGet.forEach(ele => {delegateGet('request', ele);})responseSet.forEach(ele => {delegateSet('response', ele);})responseGet.forEach(ele => {delegateGet('response', ele);})module.exports = proto;
./src/application.js
const request = require('./request');const response = require('./response');const context = require('./context');this.context = context;this.request = request;this.response = response;responseBody (ctx) {let content = ctx.body;if (typeof content === 'string') {ctx.res.end(content);} else if (typeof content === 'object') {ctx.res.end(JSON.stringify(content));}}callback() {return (req, res) => {const ctx = this.createContext(req, res);let respond = () => this.responseBody(ctx);let onError = (err) => this.onError(err, ctx);let fn = this.compose();return fn(ctx).then(respond).catch(onError)}}
参考1: https://juejin.im/post/5be3a0a65188256ccc192a87
参考2:https://juejin.im/post/5914fdce44d904006c44dfac
