国产xxxx99真实实拍_久久不雅视频_高清韩国a级特黄毛片_嗯老师别我我受不了了小说

資訊專欄INFORMATION COLUMN

解讀express 4.x源碼(1)

summerpxy / 2608人閱讀

摘要:在后續的總結中,我會繼續分析,并準備將一些值得分析的逐一解讀,也會涉及一些。從一個官方示例開始這是官方給出的一個簡單程序,運行后訪問顯示。第一行載入了框架,我們來看源代碼中的。代碼的開始定義了一個函數,函數有形參,,為回調函數。

這兩天仔細看了看express的源碼,對其的整個實現有了較清晰的認識,所以想總結一下寫出來,如果有什么不對的地方,望指出。

這是第一篇,首先介紹一個最簡單的express應用運行過程,初步分析了其在源碼中的具體實現,還沒有涉及到一些比較重要的內容比如路由組件的實現方式,中間件的觸發流程等。在后續的總結中,我會繼續分析,并準備將一些值得分析的public api逐一解讀,也會涉及一些private api

基于的版本

截止寫這篇文章時目前最新的tags是4.4.2。我是直接看的master分支。express的commits提交非常頻繁,但總體的實現思路應該不會有大的變化。其在4.x后做了較大的改動,相對于3.x最大的地方在于不再依賴connect,并移除了幾乎所有的內置中間件,具體的變動請看官方wiki的 Migrating from 3.x to 4.x 及 New features in 4.x。

從一個官方示例開始
var express = require("express");
var app = express();

app.get("/", function(req, res){
  res.send("Hello World");
});

app.listen(3000);

這是官方給出的一個簡單程序,運行后訪問localhost:3000顯示Hello World。下面我們就來仔細看看這段程序。

首先第一行

var express = require("express");

這是典型的Node.js模塊載入代碼,關于Node.js的模塊載入機制,不了解的同學建議看看樸靈的深入Node.js的模塊機制,非常有幫助。

第一行載入了express框架,我們來看源代碼中的index.js

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

好吧,還要繼續require,我們看./lib/express.js

exports = module.exports = createApplication;

從這里我們可以看出,程序的第一行express最后實際是這個createApplication函數。第二行則是運行了這個函數,然后返回值賦給了app。該函數代碼如下

var EventEmitter = require("events").EventEmitter;
var mixin = require("utils-merge");
var proto = require("./application");
var req = require("./request");
var res = require("./response");

function createApplication() {
  var app = function(req, res, next) {
    app.handle(req, res, next);
  };

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

  app.request = { __proto__: req, app: app };
  app.response = { __proto__: res, app: app };
  app.init();
  return app;
}

可以發現,這個就相當于express"main"函數,其中完成了所有創建express實例所需要的動作,并在執行完畢后返回一個函數。

代碼的開始定義了一個函數,函數有形參reqresnext為回調函數。
函數體只有一條語句,執行app.handlehandle方法在application.js文件中定義,此處是通過mixin導入(見下文),handle的代碼如下

app.handle = function(req, res, done) {
  var router = this._router;

  // final handler
  done = done || finalhandler(req, res, {
    env: this.get("env"),
    onerror: logerror.bind(this)
  });

  // no routes
  if (!router) {
    debug("no routes defined on app");

    // generate error    
    var err = new Error("No routes or middlewares have been defined");
    err.status = 500;
    done(err);
    return;
  }

  router.handle(req, res, done);
};

它的作用就是將每對[req,res]進行逐級分發,作用在每個定義好的路由及中間件上,直到最后完成,具體的過程我們會在后續進行分析。

然后來看看中間的兩行

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

mixin是在頭部的require處載入的utils-merge模塊,它的代碼如下

exports = module.exports = function(a, b){
  if (a && b) {
    for (var key in b) {
      a[key] = b[key];
    }
  }
  return a;
};

很明顯,mixin(app, proto);的作用即是將proto中所有的property全部導入進appproto在頭部的require處載入的是./lib/application.js文件,其中定義了大部分expresspublic api,如app.set,app.get,app.use...詳見官方的API文檔。
mixin(app, EventEmitter.prototype);則將Node.jsEventEmitter中的原型方法全部導入了app。

再來看接下來的兩行

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

這里定義了apprequestresponse對象,使用了對象的字面量表示法,使其分別繼承自req(頂部導入的request.js)和res(頂部導入的response.js),并反向引用了app自身。為什么要這樣做呢?這個問題我一開始想不明白,后來我就干脆把這兩行代碼刪了,運行,當然就是報錯,答案就在錯誤中的信息里。

  

TypeError: Object # has no method "send"

顯示找不到"send"方法,為什么呢?首先我們從app.get()方法看起,不熟悉的人會找不到它在源碼中的位置,其實它在application.js中是這樣的

methods.forEach(function(method){
  app[method] = function(path){
    if ("get" == method && 1 == arguments.length) return this.set(path);

    this.lazyrouter();

    var route = this._router.route(path);
    route[method].apply(route, [].slice.call(arguments, 1));
    return this;
  };
});

methods在頂部模塊引入中定義,其實是一個包含各個HTTP請求方法的數組,具體代碼在這里。
從上面的代碼中我們可以看到,這里實際上是遍歷了所有methods中定義的方法,當然其中包括get,而且get方法是被"重載"的,即當app.get();的參數只有一個時候,執行的是獲取變量的功能,否則,執行route組件中的route.get方法,將該路由和回調函數(即第二個參數)存儲進一個棧中(后續會進一步分析)。
回到原來的問題,在這里,關鍵是看中間的

this.lazyrouter();

我們看它的具體代碼

app.lazyrouter = function() {
  if (!this._router) {
    this._router = new Router({
      caseSensitive: this.enabled("case sensitive routing"),
      strict: this.enabled("strict routing")
    });

    this._router.use(query());
    this._router.use(middleware.init(this));
  }
};

它的作用是在第一次定義路由的時候初始化路由(添加基本的路由),注意最后一句用到了middleware模塊的init方法,繼續上代碼

exports.init = function(app){
  return function expressInit(req, res, next){
    if (app.enabled("x-powered-by")) res.setHeader("X-Powered-By", "Express");
    req.res = res;
    res.req = req;
    req.next = next;

    req.__proto__ = app.request;
    res.__proto__ = app.response;

    res.locals = res.locals || Object.create(null);

    next();
  };
};

它的作用是初始化requestresponse,可以看到其中用到了我所疑惑app.requestapp.respone,它使reqres繼承自了request.jsresponse.js中的定義,也因此在我去掉了那兩行代碼后會出現res.send找不到的情況。
另外,定義app.response對象時反引用自身,也使得后面在response對象中能夠通過this.app獲得所創建的express實例。

讓我們回到createApplication函數,接下來是app.init();。顯然,作用是初始化,做哪些工作呢?

app.init = function(){
  this.cache = {};
  this.settings = {};
  this.engines = {};
  this.defaultConfiguration();
};

設定了cache對象(render的時候用到),各種setting的存儲對象,engines對象(模板引擎),最后進行默認的配置,代碼有點長這里就不上了,就是做一些默認的配置。

好了,createApplication函數就是這些,當然,其中略去了很多重要的問題,比如路由組件的實現方式,中間件的觸發流程等,這我會在后續的總結中進行分析。

最開頭的官方示例中還有最后一句

app.listen(3000);

代碼如下

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

實際上是調用了Node.js原生的http模塊的CreatServer方法,API文檔說明是

  

http.createServer([requestListener])#
Returns a new web server object.

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

方法返回的是一個web server對象,其中的參數為HTTP request事件觸發后執行的函數(這里我們給的就是我們在createApplication函數中獲得的app)。
最后,返回的web server有一個監聽端口的listen方法,參數為需要監聽的端口號,本示例中即為3000

文章版權歸作者所有,未經允許請勿轉載,若此文章存在違規行為,您可以聯系管理員刪除。

轉載請注明本文地址:http://specialneedsforspecialkids.com/yun/87548.html

相關文章

  • express4.0源碼解析

    摘要:就是每一個教程里面開始教學的事例,啟動服務器的回調函數。,從入口開始分析源碼首先是把模塊的屬性全部進里面去,在把事件的屬性全部進里面去,這是為了給增加事件功能。 express4.X源碼解讀第一天 express4.X 跟3.X 有很大區別,4.X 去除了connect的依賴,3.X基于connect的中間件基本全部不能用,如果還有可以使用的,也是4.X重寫的。所以要想繼續使用這些熟悉...

    paraller 評論0 收藏0
  • 筆記:解讀express 4.x源碼

    摘要:載入了框架,我們來看源代碼中的。函數函數代碼如下代碼的開始定義了一個函數,函數有形參,,為回調函數。相應的,等同于繼承,從而讓有了事件處理的能力。 此為裁剪過的筆記版本。 原文在此:https://segmentfault.com/a/11...原文在此: https://cnodejs.org/topic/574... 感謝@YiQi ,@leijianning 帶來的好文章。我稍作...

    jzman 評論0 收藏0
  • webpack-dev-middleware@1.12.2 源碼解讀

    摘要:如果此時我們不想把文件輸出到內存里,可以通過修改的源代碼來實現。服務啟動成功。。。根據請求的,拼接出 ? webpack-dev-middleware 是express的一個中間件,它的主要作用是以監聽模式啟動webpack,將webpack編譯后的文件輸出到內存里,然后將內存的文件輸出到epxress服務器上;下面通過一張圖片來看一下它的工作原理: showImg(https:...

    yearsj 評論0 收藏0
  • express中cookie的使用和cookie-parser的解讀

    摘要:最近在研究,學著使用,開始不會用,就百度了一下,沒有百度到特別完整的解答。查閱了的,綜合了網友的博客,解讀了的源碼,以及使用和驗證,終于明白了中的使用。默認為網站域名過期時間,類型為。使用插件,后續代碼直接使用或者即可 最近在研究express,學著使用cookie,開始不會用,就百度了一下,沒有百度到特別完整的解答。查閱了express的API,綜合了網友的博客,解讀了cookie-...

    CODING 評論0 收藏0
  • 從用 void 0 代替 undefined 說起

    摘要:最近開始看源碼,并將源碼解讀放在了我的計劃中。相對于其他源碼解讀的文章,基本都會從整體設計開始講起,樓主覺得這個庫有點特殊,決定按照自己的思路,從用代替說起。源碼沒有出現注意,其實有出現一處,是為,而不是,而用代替之。 Why underscore 最近開始看 underscore源碼,并將 underscore源碼解讀 放在了我的 2016計劃 中。 閱讀一些著名框架類庫的源碼,就好...

    Cc_2011 評論0 收藏0

發表評論

0條評論

summerpxy

|高級講師

TA的文章

閱讀更多
最新活動
閱讀需要支付1元查看
<