最近一直在学习如何用原生的 Node.js 来做一个网站。在写的同时也在学习 Express 源码。

一直觉得 Express 开启服务器的方法挺有趣的,就看了一下。

在 Express 运行的时候会默认运行根目录下的 index.js,里面的源码也很简单:

module.exports = require('./lib/express');

看到其实运行了 lib/express 模块,追踪过去,看到了:

exports = module.exports = createApplication;

导出了 createApplication 这个方法:

function createApplication() {
var app = function(req, res, next) {
app.handle(req, res, next);
}; mixin(app, EventEmitter.prototype, false);
mixin(app, proto, false); app.request = { __proto__: req, app: app };
app.response = { __proto__: res, app: app };
app.init();
return app;
}

首先定义了一个方法 app,方法有三个参数 reqresnext。然后在这个函数里面又执行了 app.handle 这个函数,这个函数后面说说,涉及到了路由。同时还涉及到了创建服务器函数,下面继续。

再往下看看,会看到 mixin 这个函数,在最上面引入模块的时候,定义了这个函数名:

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');

mixin 这个函数是引用了 merge-descriptors模块,嗯,到 node_module 里面找找,或者到 github 里面找找也可以。

看源码,如果对原生JS的函数不是很熟悉的话,根本看不懂...不过我们可以到 MDN 里面查查,地址懒的贴。我也懒得去介绍里面的各种函数,这个模块的作用和 jQuery 里面的 $.extent 其实是一模一样的,将第二个参数的属性和属性值合并到第一个参数中,第三个参数如果是 false 则如果两个参数里面有属性一样,不允许覆盖,如果是 true,则覆盖第一个参数属性。

由此可见:

mixin(app, EventEmitter.prototype, false);
mixin(app, proto, false);

这两句话就很好懂了,就是将 EventEmitter.prototypeproto 的属性克隆给 app

这里看到 proto 肯定也很郁闷...这玩意从哪里来的...还是往上面看:

var proto = require('./application');

额...又是一个模块,继续追踪,第一句话:

var app = exports = module.exports = {};

唉哟,又看到了 app,这 express 命名也是有趣,都是 app 。我再看的时候也很郁闷,完全会看混淆。继续往下看,原来这个模块的作用就是给 app 添加了各种属性和函数,各种核心函数...各种看不懂的函数...

这里我们找到我们希望看到的一个 listen 这个函数:

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

好烦哟...又碰到看不懂的了,尼玛 this 是什么鬼!!!!我们从 Node.js 的 API 手册可以看到啊,明明官方都说了:

The requestListener is a function which is automatically added to the 'request' event.

this 这个参数的位置明明应该是一个函数啊,但是在 application 里面我们看到的 app 很显然就是一个 {} 对象啊,尼玛,怎么可能是一个 Function ?

好吧,没办法,我们 console.log(this) 试试,看看返回的是什么:

{ [Function]
domain: undefined,
_events: { mount: [Function: onmount] },
_maxListeners: undefined,
setMaxListeners: [Function: setMaxListeners],
getMaxListeners: [Function: getMaxListeners],
emit: [Function: emit],
addListener: [Function: addListener],
on: [Function: addListener],
once: [Function: once],
removeListener: [Function: removeListener],
removeAllListeners: [Function: removeAllListeners],
listeners: [Function: listeners],
listenerCount: [Function: listenerCount],
init: [Function: init],
defaultConfiguration: [Function: defaultConfiguration],
lazyrouter: [Function: lazyrouter],
handle: [Function: handle],
use: [Function: use],
route: [Function: route],
...

第一句居然是 Function ,是什么鬼?什么时候变成了 Function ,仔细看一下,我们发现里面好像有 EventEmitter.prototype 的属性:

EventEmitter {
domain: undefined,
_events: undefined,
_maxListeners: undefined,
setMaxListeners: [Function: setMaxListeners],
getMaxListeners: [Function: getMaxListeners],
emit: [Function: emit],
addListener: [Function: addListener],
on: [Function: addListener],
once: [Function: once],
removeListener: [Function: removeListener],
removeAllListeners: [Function: removeAllListeners],
listeners: [Function: listeners],
listenerCount: [Function: listenerCount] }

哎哟,不错哟,貌似发现了新大陆...明明在 application 这个模块没有添加这个属性啊,从哪里加的?这个时候我们应该顺其自然的想到了:

mixin(app, EventEmitter.prototype, false);

这句话,好像很突然的想到似的...是否在想明明在 express 这个模块加的,为啥在 application 里面会添加进去?OK,到了我们最关键的时候了,我们应该把 Express 的执行顺序理清楚了:

  • 在 CMD 或者 终端 执行:node app
  • 在我们自己写的 app.js 文件中执行 express(),这里就开始运行 express 模块了。
  • 进去 express 模块,运行 index.js
  • index.js 进去 lib/express.js
  • express.js 中执行 createApplication
  • createApplication 中首先定义了一个 app 的函数,其实刚才我们 console.log(this) 的时候,this 返回的就是这个 app 函数
  • 然后通过 mixin(app, EventEmitter.prototype, false);mixin(app, proto, false);EventEmitter.prototypeproto 里面的属性全部都加到 app 这个函数里面
  • 执行app.requestapp.response 这两个就是将重新定义的 requestresponse 加到 app 里面,这两个以后再说
  • 继续执行 app.init(); ,这句话就简单了啊,init 这个函数在 application.js 模块中,主要作用就是给 app 加一些初始化设置
  • 最后返回 app
  • 在我们自己写的 app.js 文件中获取到返回的 app,准备创建服务器 app.listen(3000)
  • 开始执行 app.listen 这个函数,在 app 中找到 listen,这个函数在 express.js 中已经从 proto 克隆到了 app 上了,所以,我们直接到 proto 中找 listen,也就是 application.js 中去找,找到之后就回到了我们最开始的问题:var server = http.createServer(this);this 是什么????看到这里,其实我们已经知道了,this 其实就是 var app = function(req,res,next){},OK到这里就结束了...

这么晚写这文章,感觉写了一大坨废话...最后的执行顺序才是我最想说的

Express app.listen 函数了解的更多相关文章

  1. Best practices for Express app structure

    Node和Express没有一个相对严格的文件或者是文件夹结构,因此你可以按照自己的想法来构建你的web项目,特别是对一些小的项目来说,他很容易学习. 然而当你的项目变得越来越大,所面临的情况越来越复 ...

  2. socket编程listen函数限制连接数的解决方案

    函数原型: int listen(int sockfd, int backlog); 当服务器编程时,经常需要限制客户端的连接个数,下面为问题分析以及解决办法: 下面只讨论TCP  UDP不做讨论(很 ...

  3. listen函数

    listen函数仅仅由TCP服务器调用,它做2件事: 1)当socket函数创建一个套接字时,它被假设为一个主动套接字,也就是说,它是一个将调用connect发起连接的客户套接字 listen函数把一 ...

  4. tcp/ip协议listen函数中backlog參数的含义

    listen函数的定义例如以下所看到的: #include <sys/socket.h> int accept(int sockfd, struct sockaddr * restrict ...

  5. listen()函数中backlog参数分析

    实例分析1 将服务器端的listen函数backlog设置为2,用20个客户端与服务器建立连接,查看连接的建立情况. 服务器代码: #include <stdio.h> #include& ...

  6. Python中网络编程对 listen 函数的理解

    listen函数的第一个参数时SOCKET类型的,该函数的作用是在这个SOCKET句柄上建立监听,至于有没有客户端连接进来,就需要accept函数去进行检查了,accept函数的第一个参数也是SOCK ...

  7. 网络编程socket之listen函数

    摘要:listen函数使用主动连接套接口变为被连接套接口,使得一个进程可以接受其它进程的请求,从而成为一个服务器进程.在TCP服务器编程中listen函数把进程变为一个服务器,并指定相应的套接字变为被 ...

  8. listen() 函数

    声明:本文来自网络博文的合并,文后有链接. 一.listen函数仅由TCP服务器调用 它做两件事: 1.当socket函数创建一个套接字时,它被假设为一个主动套接字,也就是说,它是一个将调用conne ...

  9. 网络编程:listen函数

    listen函数仅由TCP服务器调用,它做两件事: 当socket函数创建一个套接字时,它被假设为一个主动套接字,也就是说,它是一个将调用connect发起连接的客户套接字.listen函数把一个未连 ...

随机推荐

  1. Python+Selenium笔记(十四)鼠标与键盘事件

     (一) 前言 Webdriver高级应用的API,允许我们模拟简单到复杂的键盘和鼠标事件,如拖拽操作.快捷键组合.长按以及鼠标右键操作,都是通过使用webdriver的Python API 中的Ac ...

  2. TestLink笔记(一):环境配置+安装

    注:转载请加上原文链接,谢谢! 本文的安装环境是Windows操作系统. (一)     前期准备 1.XAMPP下载(下载5.6的版本) https://www.apachefriends.org/ ...

  3. LeetCode题解之 Implement strStr()

    1.题目描述 2.题目分析 字符串操作,注意边界条件即可. 3.代码 int strStr(string haystack, string needle) { int n = needle.size( ...

  4. myeclipse 10配置jboss 7.1.1无法启动Error: Could not create the Java Virtual Machine

    myeclipse 10中配置jboss 7.1.1,多写了个server name,结果死活启动不了.后来,发现了这个细节. 错误显示: 错误源头: 删掉Server name后,可以完美启动 小问 ...

  5. VS2015 调试 条件和操作设置

    vs 开发过程中,经常会遇到这样的场景: 例如有几千条数据,调试代码的时候需要在循环中走到name="A"的这条数据, 并且单步调试这条数据走完整个逻辑过程.这个时候调试的条件设置 ...

  6. 小程序码B接口生成出错:场景内容包含非法字符

    由于包含了非法字符,微信返回的字节不超过100字符,但是没有包含提示内容,因此很难识别发现问题所在

  7. NIO 学习笔记

    0. 介绍 参考   关于Java IO与NIO知识都在这里   ,在其基础上进行修改与补充. 1. NIO介绍 1.1 NIO 是什么 Java NIO 是 java 1.4, 之后新出的一套IO接 ...

  8. table中怎么设置两行间距

    table中设置两行间距方法: css代码实现 <style> table   {   border-collapse:   separate;   border-spacing:   1 ...

  9. BZOJ5334:[TJOI2018]数学计算(线段树)

    Description 小豆现在有一个数x,初始值为1. 小豆有Q次操作,操作有两种类型:  1 m: x = x  *  m ,输出 x%mod; 2 pos: x = x /  第pos次操作所乘 ...

  10. vue项目 使用nginx代理

    nginx是一个高性能的HTTP和反向代理服务器.因此常用来做静态资源服务器和后端的反向代理服务器.本文主要记录使用nginx去部署使用vue搭建的前端项目,项目基于vue官方的脚手架vue-cli构 ...