image.png

koa框架需要实现四个大模块,分别是:

  • 封装node http server、创建Koa类构造函数
  • 构造request、response、context对象
  • 中间件机制和剥洋葱模型的实现
  • 错误捕获和错误处理

运行脚本 app.js, 访问相应的端口, 控制台打印1,2,3,4,5

  1. const Koa = require("koa");
  2. const app = new Koa();
  3. app.use(async (ctx, next) => {
  4. console.log(1);
  5. await next();
  6. console.log(5);
  7. });
  8. app.use(async (ctx, next) => {
  9. console.log(2);
  10. await next();
  11. console.log(4);
  12. });
  13. app.use(async (ctx) => {
  14. console.log(3);
  15. ctx.body = "Hello World";
  16. });
  17. app.listen(3000);

1. 封装node http server、创建Koa类构造函数

./src/application.js

  1. const Emitter = require('events');
  2. const http = require('http');
  3. module.exports = class Application extends Emitter {
  4. constructor(options) {
  5. super();
  6. this.callbackFn;
  7. }
  8. listen(...args) {
  9. const server = http.createServer(this.callback());
  10. return server.listen(...args);
  11. }
  12. use(fn) {
  13. this.callbackFn = fn;
  14. return this;
  15. }
  16. callback() {
  17. return (req, res) => {
  18. this.callbackFn(req, res);
  19. }
  20. }
  21. }

运行脚本app.js

  1. const Koa = require("./src/application.js");
  2. const app = new Koa();
  3. app.use((req, res) => {
  4. // res.writeHead(200);
  5. res.end('hello world');
  6. });
  7. app.listen(3000);

中间件机制和剥洋葱模型的实现

image.png
./src/application.js

  1. const Emitter = require('events');
  2. const http = require('http');
  3. const compose = require('./compose.js')
  4. module.exports = class Application extends Emitter {
  5. constructor(options) {
  6. super();
  7. this.middleware = [];
  8. this.compose = compose;
  9. }
  10. listen(...args) {
  11. const server = http.createServer(this.callback());
  12. return server.listen(...args);
  13. }
  14. use(fn) {
  15. this.middleware.push(fn);
  16. return this;
  17. }
  18. responseBody (ctx) {}
  19. onError (err, ctx) {}
  20. callback() {
  21. return (req, res) => {
  22. const ctx = "";
  23. let respond = () => this.responseBody(ctx);
  24. let onError = (err) => this.onError(err, ctx);
  25. let fn = this.compose();
  26. return fn(ctx).then(respond).catch(onError)
  27. }
  28. }
  29. }

./src/compose.js

  1. module.exports = compose
  2. function compose (middleware) {
  3. return async ctx => {
  4. function createNext(middleware, oldNext) {
  5. return async() => {
  6. await middleware(ctx, oldNext);
  7. }
  8. }
  9. let len = this.middleware.length;
  10. let next = async() => {
  11. return Promise.resolve();
  12. }
  13. for (let i = len - 1; i >= 0; i--) {
  14. let currentMiddleware = this.middleware[i];
  15. next = createNext(currentMiddleware, next);
  16. }
  17. await next();
  18. }
  19. }

运行脚本./app.js, 访问依次打印1,2,3, 4,5

构造request、response、context对象

./src/request.js

  1. let url = require('url');
  2. module.exports = {
  3. get query() {
  4. return url.parse(this.req.url, true).query;
  5. }
  6. }

./src/response.js

  1. module.exports = {
  2. get body () {
  3. return this._body;
  4. },
  5. set body (data) {
  6. this._body = data;
  7. },
  8. get status () {
  9. return this.res.statusCode;
  10. },
  11. set status (statusCode) {
  12. this.res.statusCode = statusCode;
  13. }
  14. }

./src/context.js

  1. let proto = {};
  2. function delegateSet(property, name) {
  3. proto.__defineSetter__(name, function(val) {
  4. this[property][name] = val;
  5. })
  6. }
  7. function delegateGet (property, name) {
  8. proto.__defineGetter__(name, function() {
  9. return this[property][name];
  10. })
  11. }
  12. let requestSet = [];
  13. let requestGet = ['query'];
  14. let responseSet = ['body', 'status'];
  15. let responseGet = responseSet;
  16. requestSet.forEach(ele => {
  17. delegateSet('request', ele);
  18. })
  19. requestGet.forEach(ele => {
  20. delegateGet('request', ele);
  21. })
  22. responseSet.forEach(ele => {
  23. delegateSet('response', ele);
  24. })
  25. responseGet.forEach(ele => {
  26. delegateGet('response', ele);
  27. })
  28. module.exports = proto;

./src/application.js

  1. const request = require('./request');
  2. const response = require('./response');
  3. const context = require('./context');
  4. this.context = context;
  5. this.request = request;
  6. this.response = response;
  7. responseBody (ctx) {
  8. let content = ctx.body;
  9. if (typeof content === 'string') {
  10. ctx.res.end(content);
  11. } else if (typeof content === 'object') {
  12. ctx.res.end(JSON.stringify(content));
  13. }
  14. }
  15. callback() {
  16. return (req, res) => {
  17. const ctx = this.createContext(req, res);
  18. let respond = () => this.responseBody(ctx);
  19. let onError = (err) => this.onError(err, ctx);
  20. let fn = this.compose();
  21. return fn(ctx).then(respond).catch(onError)
  22. }
  23. }

参考1: https://juejin.im/post/5be3a0a65188256ccc192a87
参考2:https://juejin.im/post/5914fdce44d904006c44dfac