zoukankan      html  css  js  c++  java
  • koa2 源码解读 application

    koa2的源码比较简单,重点解读aplication,

    其中context源码比较简单,主要是一些error cookies等,重点可以关注下delegate,delegate模块中,主要通过prototype的方式进行属性的增加。

    request和response两个模块都是get set的一些基础api及封装的node原始方法

    application

    'use strict';   //严格模式
    
    /**
     * Module dependencies.
     */
    
    const isGeneratorFunction = require('is-generator-function');
    const debug = require('debug')('koa:application');
    const onFinished = require('on-finished');
    const response = require('./response');
    const compose = require('koa-compose');
    const isJSON = require('koa-is-json');
    const context = require('./context');
    const request = require('./request');
    const statuses = require('statuses');
    const Emitter = require('events');
    const util = require('util');
    const Stream = require('stream');
    const http = require('http');
    const only = require('only');
    const convert = require('koa-convert');
    const deprecate = require('depd')('koa');
    const { HttpError } = require('http-errors');
    
    /**
     * constructor()  构造函数
     * listen()  调用原生http模块创建服务并监听
     * use()  中间件处理
     * callback() http请求的回调函数
     * handleRequest() 请求真正的回调函数
     * createContext() 创建上下文对象
     * respond()  所有中间件处理完后自动响应
     * onerror() 处理错误信息
     * 
     */
    
    /**
     * Expose `Application` class.
     * Inherits from `Emitter.prototype`.
     */
    
    module.exports = class Application extends Emitter {
      /**
       * Initialize a new `Application`.
       *
       * @api public
       */
    
      /**
        *
        * @param {object} [options] Application options
        * @param {string} [options.env='development'] Environment
        * @param {string[]} [options.keys] Signed cookie keys
        * @param {boolean} [options.proxy] Trust proxy headers
        * @param {number} [options.subdomainOffset] Subdomain offset
        *
        */
    
      constructor(options) {
        super();
        options = options || {};
        this.proxy = options.proxy || false;       //是否允许跨域
        this.subdomainOffset = options.subdomainOffset || 2;   // 子域名允许请求几级连接
        this.env = options.env || process.env.NODE_ENV || 'development'; //node的执行环境
        if (options.keys) this.keys = options.keys; 
        this.middleware = [];                     //所有的中间件的存入
        this.context = Object.create(context);    //每次实例化都重新赋值,为保证多次实例化时保持不冲突,和单例模式成反例
        this.request = Object.create(request);
        this.response = Object.create(response);
        if (util.inspect.custom) {
          this[util.inspect.custom] = this.inspect;    //保存88行代码中的内容
        }
      }
    
      /**
       * Shorthand for:
       *
       *    http.createServer(app.callback()).listen(...)
       *
       * @param {Mixed} ...
       * @return {Server}
       * @api public
       */
    
      listen(...args) {
        debug('listen');
        const server = http.createServer(this.callback());  //原生http模块创建服务并监听  
        return server.listen(...args);
      }
    
      /**
       * Return JSON representation.
       * We only bother showing settings.
       *
       * @return {Object}
       * @api public
       */
    
      toJSON() {
        return only(this, [       //only 对传入的数据使用reduce进行重组
          'subdomainOffset',
          'proxy',
          'env'
        ]);
      }
    
      /**
       * Inspect implementation.
       *
       * @return {Object}
       * @api public
       */
    
      inspect() {
        return this.toJSON();  //数据重组
      }
    
      /**
       * Use the given middleware `fn`.
       *
       * Old-style middleware will be converted.
       *
       * @param {Function} fn
       * @return {Application} self
       * @api public
       */
    
      use(fn) {
        if (typeof fn !== 'function') throw new TypeError('middleware must be a function!');//必须是一个function
        if (isGeneratorFunction(fn)) {
          deprecate('Support for generators will be removed in v3. ' +
                    'See the documentation for examples of how to convert old middleware ' +
                    'https://github.com/koajs/koa/blob/master/docs/migration.md');
          fn = convert(fn);
        }
        debug('use %s', fn._name || fn.name || '-');   //DEBUG=koa* node --harmony app.js 调试时输出中间件调用及时长
        this.middleware.push(fn);  //将中间件加入到middleware数组中
        return this;
      }
    
      /**
       * Return a request handler callback
       * for node's native http server.
       *
       * @return {Function}
       * @api public
       */
    
      callback() {
        const fn = compose(this.middleware);   //将这些中间件组合后拿到执行链函数fn
    
        if (!this.listenerCount('error')) this.on('error', this.onerror); //如果没有监听则报错
    
        const handleRequest = (req, res) => { //事件处理函数
          const ctx = this.createContext(req, res);  //创建一个ctx
          return this.handleRequest(ctx, fn);        //交给157行的handleRequest
        };
    
        return handleRequest;
      }
    
      /**
       * Handle request in callback.
       *
       * @api private
       */
    
      handleRequest(ctx, fnMiddleware) {
        const res = ctx.res;
        res.statusCode = 404;                    //初始赋值
        const onerror = err => ctx.onerror(err);
        const handleResponse = () => respond(ctx);   //211行详解
        onFinished(res, onerror);
        return fnMiddleware(ctx).then(handleResponse).catch(onerror);
      }
    
      /**
       * Initialize a new context.
       *
       * @api private
       */
    
      createContext(req, res) {
        const context = Object.create(this.context);//通过context对象的原型创建
        const request = context.request = Object.create(this.request);//通过request对象的原型创建,this.request指的是原生的request,修改this.request中的属性就是修改原生的对应的属性数据
        const response = context.response = Object.create(this.response);//通过response对象的原型创建
        context.app = request.app = response.app = this;    //传递
        context.req = request.req = response.req = req;
        context.res = request.res = response.res = res;
        request.ctx = response.ctx = context;
        request.response = response;  //交叉传递
        response.request = request;
        context.originalUrl = request.originalUrl = req.url;
        context.state = {};
        return context;
      }
    
      /**
       * Default error handler.
       *
       * @param {Error} err
       * @api private
       */
    
      onerror(err) {
        if (!(err instanceof Error)) throw new TypeError(util.format('non-error thrown: %j', err));   //检测err不是Error实例时,创建一个Error的实例
    
        if (404 == err.status || err.expose) return; 
        if (this.silent) return;
    
        const msg = err.stack || err.toString(); //将err堆栈的信息拿出来
        console.error();                              //控制台打印Error信息
        console.error(msg.replace(/^/gm, '  '));
        console.error();
      }
    };
    
    /**
     * Response helper.
     */
    
    function respond(ctx) {
      // allow bypassing koa
      if (false === ctx.respond) return;   //允许绕过KOA 为写入原始的res对象而不是让koa处理你的rsponse
    
      if (!ctx.writable) return; 
    
      const res = ctx.res;       
      let body = ctx.body;       //外面Middleware传入的body数据
      const code = ctx.status;   //当前状态码
    
      // ignore body
      if (statuses.empty[code]) {     //当前状态码是空的,则清掉body并结束
        // strip headers
        ctx.body = null;
        return res.end();
      }
    
      if ('HEAD' == ctx.method) {        //head部分
        if (!res.headersSent && isJSON(body)) {      //判断当前header没有被发送并且是 重组后的json数据
          ctx.length = Buffer.byteLength(JSON.stringify(body));   //则重新序列化 取长度
        }
        return res.end();
      }
    
      // status body
      if (null == body) {  // body部分  不为null
        if (ctx.req.httpVersionMajor >= 2) {  //根据http major的版本  分别对body进行初始化
          body = String(code);
        } else {
          body = ctx.message || String(code);
        }
        if (!res.headersSent) {
          ctx.type = 'text';
          ctx.length = Buffer.byteLength(body);
        }
        return res.end(body);
      }
                                                            //以下为支持各种格式的body
      // responses
      if (Buffer.isBuffer(body)) return res.end(body);
      if ('string' == typeof body) return res.end(body);
      if (body instanceof Stream) return body.pipe(res);
    
      // body: json
      body = JSON.stringify(body);
      if (!res.headersSent) {
        ctx.length = Buffer.byteLength(body);
      }
      res.end(body);
    }
    
    /**
     * Make HttpError available to consumers of the library so that consumers don't
     * have a direct dependency upon `http-errors`
     */
    module.exports.HttpError = HttpError;
  • 相关阅读:
    单线程的JavaScript是如何实现异步的
    前端优化之 -- 使用 require.context 让项目实现路由自动导入
    插入排序
    选择排序
    冒泡排序
    强缓存和协商缓存
    ES6 Set求两个数组的并集、交集、差集;以及对数组去重
    实现一个new操作符
    我理解的浅拷贝和深拷贝
    javascript专题系列--js乱序
  • 原文地址:https://www.cnblogs.com/itstone/p/11765795.html
Copyright © 2011-2022 走看看