Node框架Koa源码实现

313 阅读2分钟

Koa的源码大致分为四个文件application.jscontext.jsrequest.jsresponse.js.下面是每个文件的源码实现加注释。

  • context.js

context文件主要是实现获取属性的代理。例如:ctx.path -> ctx.request.path的映射。

    let context = {};
    // 如果去context 上去值 我希望去context.request上取
    // js有一个不常用的方法   Object.defineProperty变种
    function defineGetter(key,property) { // 定义获取器
      context.__defineGetter__(property, function () {
        return this[key][property];
      })
    }
    // 给某个属性定义setter
    function defineSetter(key,property) {
      context.__defineSetter__(property, function (value) {
        this[key][property] = value
      })
    }
    // 代理 把取属性的值 通过request来获取
    defineGetter('request','path');
    defineGetter('request','url');
    defineGetter('request','query');
    // ctx.body => ctx.response.body;
    defineGetter('response','body');
    // ctx.body = '100'  ctx.response.body = '100'
    defineSetter('response','body');
    
    
    module.exports = context
  • request.js

处理请求的一些属性,例如:ctx.request.url -> ctx.request.req.url

    let url = require('url')
    let request = {
      get url(){
        return this.req.url;
      },
      get path(){
        let { pathname } = url.parse(this.req.url);
        return pathname
      },
      get query() {
        let { query } = url.parse(this.req.url,true);
        return query
      }
    };
    
    
    
    module.exports = request;

  • response.js

处理响应的一些属性。例如body

    let response = { // 主要是封装关于响应的内容
      set body(value){
        this.res.statusCode = 200; // 表明成功了
        this._body = value;
      },
      get body(){
        return this._body
      }
    };
    
    
    
    
    module.exports = response;
  • application.js
    let http = require('http');
    let path = require('path');
    let fs = require('fs');
    let context = require('./context');
    let request = require('./request');
    let response = require('./response');
    let EventEmitter = require('events');
    let Stream = require('stream');
    class Koa extends EventEmitter{
      constructor() {
        super();
        this.middleware; // Object.create(null); 没有原型的对象
        this.context = Object.create(context);
        this.request = Object.create(request);
        this.response = Object.create(response);
        this.middlewares = [];
      }
      // 注册中间件的方法
      use(fn) {
        this.middlewares.push(fn);
      }
      // 创建上下文
      createContext(req, res) { // 自己封装request 和response属性
        let ctx = this.context;
        ctx.request = this.request;
        ctx.req = ctx.request.req = req; // 请求相关的
    
        ctx.response = this.response;
        ctx.res = ctx.response.res = res; // 响应相关的
        return ctx;
      }
      // 组合方法
      compose(middles, ctx) {
        function dispatch(index) {
          // 如果没有注册中间件 需要返回一个成功的promise
          if(index === middles.length) return Promise.resolve();
          let middle = middles[index];
          // 让第一个函数执行完 如果有异步要看一看 有没有await, 必须要返回一个promise 
          return Promise.resolve(middle(ctx, () => dispatch(index + 1)));
        }
        return dispatch(0);
      }
      // 处理用户请求到来时
      handleRequest(req, res) {
        let ctx = this.createContext(req, res);
        res.statusCode = 404; //默认我就认为你返回的404没有调用ctx.body;
        let fn = this.compose(this.middlewares, ctx);
        // 把所有的函数进行组合 当组合的函数执行成功后 把最终的结果进行响应
        fn.then(()=>{
          if (!ctx.body){
            res.end('Not Found')
          } else if (ctx.body instanceof Stream){
            res.setHeader('Content-Type','text/html;charset=utf-8')
            ctx.body.pipe(res);
          }else if (typeof ctx.body ==='object'){
            res.setHeader('Content-Type','application/json;charset=utf-8');
            res.end(JSON.stringify(ctx.body));
          }else{ 
            res.end(ctx.body); // 用res.end结束
          }
        }).catch(err=>{
          this.emit('error',err);
        })
      }
      listen(...args) {
        // 启动服务
        let server = http.createServer(this.handleRequest.bind(this));
        server.listen(...args);
      }
    }
    module.exports = Koa;