.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模块,看来还是不能完全脱离,偷偷用了一个。

 

  完结。

posted @ 2018-04-13 10:25  书生小龙  阅读(843)  评论(2编辑  收藏  举报