nodejs express 框架解密2-如何建立一個app

yupeng發表於2013-12-19

本文是基於express 3.4.6 的

1.在我們的app.js 檔案裡面有這麼幾行

 

http.createServer(app).listen(app.get('port'), function(){
  console.log('Express server listening on port ' + app.get('port'));
});

這個其實是呼叫http模組 的 createServer 函式建立一個服務,然後監聽埠的。

2. 我們再去看看express 的入口檔案 

/**
 * Module dependencies.
 */

var connect = require('connect')
  , proto = require('./application')
  , Route = require('./router/route')
  , Router = require('./router')
  , req = require('./request')
  , res = require('./response')
  , utils = connect.utils;

/**
 * Expose `createApplication()`.
 */

exports = module.exports = createApplication;

/**
 * Expose mime.
 */

exports.mime = connect.mime;

/**
 * Create an express application.
 *
 * @return {Function}
 * @api public
 */

function createApplication() {
  var app = connect();
  //將application中的方法全部拷貝到connect物件上去。
  utils.merge(app, proto);
  //設定app 的request物件的原型為req,本身的屬性為connect物件
  app.request = { __proto__: req, app: app };
  //設定app的response物件原型為res ,本身的屬性為connect物件
  app.response = { __proto__: res, app: app };
  //呼叫application中的方法init
  app.init();
  return app;
}

/**
 * Expose connect.middleware as express.*
 * for example `express.logger` etc.
 */
/**
 * 載入connect模組中得所有中介軟體
 */
for (var key in connect.middleware) {
  Object.defineProperty(
      exports
    , key
    , Object.getOwnPropertyDescriptor(connect.middleware, key));
}

/**
 * Error on createServer().
 */
/**
 *  將建立伺服器的方法輸出
 * @returns {Function}
 */
exports.createServer = function(){
  console.warn('Warning: express.createServer() is deprecated, express');
  console.warn('applications no longer inherit from http.Server,');
  console.warn('please use:');
  console.warn('');
  console.warn('  var express = require("express");');
  console.warn('  var app = express();');
  console.warn('');
  //載入建立應用程式的方法,開始建立application
  return createApplication();
};

/**
 * Expose the prototypes.
 */

exports.application = proto;
exports.request = req;
exports.response = res;

/**
 * Expose constructors.
 */

exports.Route = Route;
exports.Router = Router;

// Error handler title

exports.errorHandler.title = 'Express';

可以看到exports = module.exports = createApplication;將這個作為模組匯出了,作為一個建構函式。

這個函式是: 

function createApplication() {
  var app = connect();
  //將application中的方法全部拷貝到connect物件上去。
  utils.merge(app, proto);
  //設定app 的request物件的原型為req,本身的屬性為connect物件
  app.request = { __proto__: req, app: app };
  //設定app的response物件原型為res ,本身的屬性為connect物件
  app.response = { __proto__: res, app: app };
  //呼叫application中的方法init
  app.init();
  return app;
}

首先呼叫connect 元件app,於是將proto 上該有的方法都拷貝到app上去。proto是神馬麼?它就是  proto = require('./application')  application.js 輸出的“app” 物件 所有得函式,

接著將req,res 作為 元件app 的request,response 的原型,同時將app作為他們的一個屬性,為什麼要這麼做呢?後面就會看到。最後呼叫app.init()方法,這個其實是呼叫application

中的init方法。

3.application.js 

app.init = function(){
  this.cache = {};
  this.settings = {};
  this.engines = {};
  //預設配置
  this.defaultConfiguration();
};

我們看到他是直接呼叫defaultConfiguration 方法的。我們再去看看defaultConfiguration方法的實現

app.defaultConfiguration = function(){
  // default settings
  this.enable('x-powered-by');
  this.enable('etag');
  this.set('env', process.env.NODE_ENV || 'development');
  this.set('subdomain offset', 2);
  debug('booting in %s mode', this.get('env'));

  // implicit middleware
  //呼叫中介軟體
  this.use(connect.query());
  this.use(middleware.init(this));

  // inherit protos
  //繼承原型
  this.on('mount', function(parent){
    this.request.__proto__ = parent.request;
    this.response.__proto__ = parent.response;
    this.engines.__proto__ = parent.engines;
    this.settings.__proto__ = parent.settings;
  });

  //router
  //路由
  this._router = new Router(this);
  this.routes = this._router.map;
  this.__defineGetter__('router', function(){
    this._usedRouter = true;
    this._router.caseSensitive = this.enabled('case sensitive routing');
    this._router.strict = this.enabled('strict routing');
    return this._router.middleware;
  });

  // setup locals
  this.locals = locals(this);

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

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

  this.configure('development', function(){
    this.set('json spaces', 2);
  });

  this.configure('production', function(){
    this.enable('view cache');
  });
};

從程式碼中可以看到,它首先呼叫中介軟體,中介軟體的作用主要是改寫改寫request,response 請求的。將這2個請求匯出,方便後面的模板渲染。然後再呼叫路由模組。路由模組只要是根據path

呼叫路由分發函式分發路由,執行callback,最後呼叫view 模組,渲染我們的模板。

相關文章