zoukankan      html  css  js  c++  java
  • .1-浅析express源码之入口文件

      鸽了鸽了,webpack源码大垃圾,看了那么久,感觉自己越来越渣……还是换个口味,node了解一下?

      尝试从express框架源码入手,学习一下node的http模块相关的知识。

    入口文件

      先从框架的主文件入手,该JS文件包含三大部分:

    1、外部/工具模块引入与属性挂载

    2、主函数定义

    3、中间件的分离提示

      

      首先是第一块,具体的相关代码如下:

    var bodyParser = require('body-parser')
    var EventEmitter = require('events').EventEmitter;
    var mixin = require('merge-descriptors');
    var proto = require('./application');
    var Route = require('./router/route');
    var Router = require('./router');
    var req = require('./request');
    var res = require('./response');
    
    // 内部模块
    
    exports.application = proto;
    exports.request = req;
    exports.response = res;
    
    // 构造方法
    
    exports.Route = Route;
    exports.Router = Router;
    
    // 中间件
    
    exports.json = bodyParser.json
    exports.query = require('./middleware/query');
    exports.static = require('serve-static');
    exports.urlencoded = bodyParser.urlencoded

      昨天正正经经的刷了一遍官方文档,在API的那一块,很暴力的把express分为了5个模块:

    1、express本身

    2、Application

    3、Request

    4、Response

    5、Router

      从源码来看也正是这样的,值得注意的是,express内部自带了body-parser模块,并且将该模块用来解析application/json与application/x-www-form-urlencoded形式的方法添加到了express上面。

      EventEmitter是node内置事件模块,不必多讲。剩下的就是mixin方法,可以看下源码:

    var hasOwnProperty = Object.prototype.hasOwnProperty
    
    module.exports = function merge(dest, src, redefine) {
        // 错误处理
        if (!dest) throw new TypeError('argument dest is required')
        if (!src) throw new TypeError('argument src is required')
        // 默认为true
        if (redefine === undefined) redefine = true
    
        Object.getOwnPropertyNames(src).forEach(function forEachOwnPropertyName(name) {
            // redefine参数的作用是在目标对象与源对象有冲突键时 是否进行覆盖定义
            if (!redefine && hasOwnProperty.call(dest, name)) return
    
            // 复制所有键 包含不可枚举的
            var descriptor = Object.getOwnPropertyDescriptor(src, name)
            Object.defineProperty(dest, name, descriptor)
        })
        return dest
    }

      所以说,在引入express模块后,除了直接执行获取app实例,还可以调用上面的一些方法。

      第二块就来看主函数的定义了,大部分的情况下,express的使用不外乎下面两行代码:

    var express = require('express');
    var app = express();

      也就是express模块本身在引入后是一个函数,而函数的源码如下:

    function createApplication() {
        // 返回的app实例也是一个函数
        var app = function(req, res, next) {
            app.handle(req, res, next);
        };
        // 目标对象属性的复制
        mixin(app, EventEmitter.prototype, false);
        mixin(app, proto, false);
    
        // 挂载req、res的属性
        app.request = Object.create(req, {
            app: { configurable: true, enumerable: true, writable: true, value: app }
        });
        app.response = Object.create(res, {
            app: { configurable: true, enumerable: true, writable: true, value: app }
        });
        
        // 初始化 
        // 该方法来源于上面的proto
        app.init();
        return app;
    }

      函数十分简单,首先定义了一个函数,然后将EventEmitter、proto(application)上面的属性添加到函数上,把request、response的原型设置为引入的内部模块req、res,调用init初始化方法后,返回app。

    4.18后记,补充一下这里的知识点:

      这里返回函数是必要的,首先参照正常情况下创建node服务器的代码:

    let http = require('http');
    http.createServer((req, res) => {
        // ...
    }).listen(9123);

      可以看出,第二步的方法调用接受一个函数,通常我们会在这处理请求并进行响应。

      而app.listen方法(这里提前讲一下)的代码如下:

    app.listen = function listen() {
        var server = http.createServer(this);
        return server.listen.apply(server, arguments);
    };

      可以看到,基本上就是原生的方法,对应传进去的函数变成了this,this指向什么呢?就是生成的函数,当有请求时,触发的函数就是app.handle方法。

      涉及的handle、init方法均来源于混入的proto中,这个后面再看。

      第三块就是4.x的一个变化:Express 4 不再依赖 Connect,而且从内核中移除了除 express.static 外的所有内置中间件。

      从代码来看就很直白:

    // 分号确实是源码里的
    // 因为上一行代码没有分号
    ;[
        'bodyParser',
        'compress',
        'cookieSession',
        'session',
        'logger',
        'cookieParser',
        'favicon',
        'responseTime',
        'errorHandler',
        'timeout',
        'methodOverride',
        'vhost',
        'csrf',
        'directory',
        'limit',
        'multipart',
        'staticCache',
      ].forEach(function (name) {
        // 每次尝试在express上访问这些属性将会报错
        Object.defineProperty(exports, name, {
          get: function () {
            throw new Error('Most middleware (like ' + name + ') is no longer bundled with Express and must be installed separately. Please see https://github.com/senchalabs/connect#middleware.');
          },
          configurable: true
        });
      });

      对原有的中间件属性访问将会报错……但是问题是内部又引入了body-parser模块,看来还是不能完全脱离,偷偷用了一个。

      完结。

  • 相关阅读:
    关于高等代数的证明题
    关于微小摄动法的专题讨论
    关于数学分析的数学竞赛题
    关于高等代数的数学竞赛题
    关于幂等阵与幂幺阵的专题讨论
    关于幂零阵与秩1阵的专题讨论
    关于可交换阵与数量阵的专题讨论
    关于等价标准形的专题讨论
    css制作圣诞树
    Integer.parseInt() 错误
  • 原文地址:https://www.cnblogs.com/QH-Jimmy/p/8818028.html
Copyright © 2011-2022 走看看