Express app.listen 函數了解


最近一直在學習如何用原生的 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到這里就結束了...

這么晚寫這文章,感覺寫了一大坨廢話...最后的執行順序才是我最想說的


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM