解读express 4.x源码

本文详细解读了Express 4.x的源码,包括核心概念如this、闭包和原型链的理解,以及对Express API的了解。文章引导读者跟随源码,探讨了app的初始化、request和response、常用方法的实现,如app.use()和app.get()等。通过分析route、router、layer之间的关系,帮助读者理解请求处理的流程和堆栈结构。阅读源码有助于深化对JavaScript和Express原理的认识。
摘要由CSDN通过智能技术生成

阅读源码之前需要读者对javascript的this变量,闭包以及原型链有深刻理解,因为源码涉及很多对这方面知识运用,同时读者要对express的api比较了解,可以阅读这里来了解api的功能。 在读文章之前,请读者先下载express源码,跟着我的思路阅读代码。

这里写图片描述

图1

首先来看看上面这个简单的例子,我们从简单入手。
第一行引入了 express模块,我们进入 expressindex.js,看到如下代码
这里写图片描述

图2

这里的程序只是引用 lib/express文件,我们进入 lib/express文件看看
这里写图片描述

图3

图1程序的第一行 express最后返回一个 createApplication函数,第二行则是运行了这个返回的函数,然后返回值赋给了 app,可以发现,这个就相当于 expressmain函数,其中完成了所有创建express实例所需要的动作。
图3中代码的开始定义了一个函数,并把函数返回给app变量,函数有形参 req,res,next为回调函数,函数体只有一条语句,执行 app.handle,handle方法在 application.js文件中定义,此处是通过 mixin导入(见下文), handle的代码如下
这里写图片描述

图4

handle函数的作用就是函数的入口,具体的过程如下,我看看看图1这句代码 app.listen(3000);。我们进入listen函数看看
这里写图片描述

图5

http.createServer(this) 这句代码可以看出,一旦有请求访问3000这个端口,就会调用 this , this 指的是 app , 程序会执行 app() 调用图3的 handle 方法,这里就是整个app的函数入口了,先把函数入口记住,等后面会回来讲解。
继续看图3的代码

app.request = { __proto__: req, app: app };    app.response = { __proto__: res, app: app };

这两句代码相当于给app定义了request,response 的属性,同时让这两个属性的原型指向req,res

var req = require('./request');
var res = require('./response');

这里的request和response不是我们此篇文章的重点。
我们接着图3的代码往下看

app.init();

进入init函数看看

app.init = function init() {
   
  this.cache = {};
  this.engines = {};
  this.settings = {};
  this.defaultConfiguration();
};
/**
 * Initialize application configuration.
 * @private
 */
app.defaultConfiguration = function defaultConfiguration() {
   
  var env = process.env.NODE_ENV || 'development';
  // default settings
  this.enable('x-powered-by');
  this.set('etag', 'weak');
  this.set('env', env);
  this.set('query parser', 'extended');
  this.set('subdomain offset', 2);
  this.set('trust proxy', false);

  // trust proxy inherit back-compat
  Object.defineProperty(this.settings, trustProxyDefaultSymbol, {
    configurable: true,
    value: true
  });

  debug('booting in %s mode', env);

  this.on('mount', function onmount(parent) {
   
    // inherit trust proxy
    if (this.settings[trustProxyDefaultSymbol] === true
      && typeof parent.settings['trust proxy fn'] === 'function') {
      delete this.settings['trust proxy'];
      delete this.settings['trust proxy fn'];
    }

    // inherit protos
    this.request.__proto__ = parent.request;
    this.response.__proto__ = parent.response;
    this.engines.__proto__ = parent.engines;
    this.settings.__proto__ = parent.settings;
  });

  // setup locals
  this.locals = Object.create(null);

  // top-most app is mounted at /
  this.mountpath = '/';

  // default locals
  this.locals.settings = this.settings;

  // default configuration
  this.set('view', View);
  this.set('views', resolve('views'));
  this.set('jsonp callback name', 'callback');

  if (env === 'production') {
    this.enable('view cache');
  }

  Object.defineProperty(this, 'router', {
    get: function() {
   
      throw new Error('\'app.router\' is deprecated!\nPlease see the 3.x to 4.x migration guide for details on how to update your app.');
    }
  });
};

这里的代码就是给app做了一些初始化,给他设置了一些参数初始值。文章只讲express一些主要的方法,不可面面俱到,想对这个init函数的细节深入了解的需要读者自己去了解源码。比如set函数和enable函数。

读到这里,可能有很多读者不知道要如何继续往下读了,这里我给大家一个建议,读任何源码都可以从常用的api入手,前面讲了那么多,却还没开始讲api是如何定义的。说道expressapi,相信所有人都会首先想到use函数,那我们就来看看use函数是如何定义的。代码如下。

这里写图片描述

图6

use函数,首先对传进来的参数做了验证,判断第一个参数是不是路径,如果是路径就取出后面的参数做遍历,如果不是路径就直接拿参数做遍历,在遍历之前先调用this.lazyrouter(),我们来看看lazyrouter是怎么定义的,代码如下。

这里写图片描述

图7

先判段 _router是否存在,不存在就新建一个 RoutercaseSensitive 表示Router是否要对路径的大小写敏感
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值