鴿了鴿了,webpack源碼大垃圾,看了那麼久,感覺自己越來越渣……還是換個口味,node瞭解一下? 嘗試從express框架源碼入手,學習一下node的http模塊相關的知識。 入口文件 先從框架的主文件入手,該JS文件包含三大部分: 1、外部/工具模塊引入與屬性掛載 2、主函數定義 3、中間件的 ...
鴿了鴿了,webpack源碼大垃圾,看了那麼久,感覺自己越來越渣……還是換個口味,node瞭解一下?
嘗試從express框架源碼入手,學習一下node的http模塊相關的知識。
入口文件
先從框架的主文件入手,該JS文件包含三大部分:
1、外部/工具模塊引入與屬性掛載
2、主函數定義
3、中間件的分離提示
首先是第一塊,具體的相關代碼如下:
var bodyParser = require('body-parser') 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'); // 內部模塊 exports.application = proto; exports.request = req; exports.response = res; // 構造方法 exports.Route = Route; exports.Router = Router; // 中間件 exports.json = bodyParser.json exports.query = require('./middleware/query'); exports.static = require('serve-static'); exports.urlencoded = bodyParser.urlencoded
昨天正正經經的刷了一遍官方文檔,在API的那一塊,很暴力的把express分為了5個模塊:
1、express本身
2、Application
3、Request
4、Response
5、Router
從源碼來看也正是這樣的,值得註意的是,express內部自帶了body-parser模塊,並且將該模塊用來解析application/json與application/x-www-form-urlencoded形式的方法添加到了express上面。
EventEmitter是node內置事件模塊,不必多講。剩下的就是mixin方法,可以看下源碼:
var hasOwnProperty = Object.prototype.hasOwnProperty module.exports = function merge(dest, src, redefine) { // 錯誤處理 if (!dest) throw new TypeError('argument dest is required') if (!src) throw new TypeError('argument src is required') // 預設為true if (redefine === undefined) redefine = true Object.getOwnPropertyNames(src).forEach(function forEachOwnPropertyName(name) { // redefine參數的作用是在目標對象與源對象有衝突鍵時 是否進行覆蓋定義 if (!redefine && hasOwnProperty.call(dest, name)) return // 複製所有鍵 包含不可枚舉的 var descriptor = Object.getOwnPropertyDescriptor(src, name) Object.defineProperty(dest, name, descriptor) }) return dest }
所以說,在引入express模塊後,除了直接執行獲取app實例,還可以調用上面的一些方法。
第二塊就來看主函數的定義了,大部分的情況下,express的使用不外乎下麵兩行代碼:
var express = require('express'); var app = express();
也就是express模塊本身在引入後是一個函數,而函數的源碼如下:
function createApplication() { // 返回的app實例也是一個函數 var app = function(req, res, next) { app.handle(req, res, next); }; // 目標對象屬性的複製 mixin(app, EventEmitter.prototype, false); mixin(app, proto, false); // 掛載req、res的屬性 app.request = Object.create(req, { app: { configurable: true, enumerable: true, writable: true, value: app } }); app.response = Object.create(res, { app: { configurable: true, enumerable: true, writable: true, value: app } }); // 初始化 // 該方法來源於上面的proto app.init(); return app; }
函數十分簡單,首先定義了一個函數,然後將EventEmitter、proto(application)上面的屬性添加到函數上,把request、response的原型設置為引入的內部模塊req、res,調用init初始化方法後,返回app。
4.18後記,補充一下這裡的知識點:
這裡返回函數是必要的,首先參照正常情況下創建node伺服器的代碼:
let http = require('http'); http.createServer((req, res) => { // ... }).listen(9123);
可以看出,第二步的方法調用接受一個函數,通常我們會在這處理請求併進行響應。
而app.listen方法(這裡提前講一下)的代碼如下:
app.listen = function listen() { var server = http.createServer(this); return server.listen.apply(server, arguments); };
可以看到,基本上就是原生的方法,對應傳進去的函數變成了this,this指向什麼呢?就是生成的函數,當有請求時,觸發的函數就是app.handle方法。
涉及的handle、init方法均來源於混入的proto中,這個後面再看。
第三塊就是4.x的一個變化:Express 4 不再依賴 Connect,而且從內核中移除了除 express.static
外的所有內置中間件。
從代碼來看就很直白:
// 分號確實是源碼里的 // 因為上一行代碼沒有分號 ;[ 'bodyParser', 'compress', 'cookieSession', 'session', 'logger', 'cookieParser', 'favicon', 'responseTime', 'errorHandler', 'timeout', 'methodOverride', 'vhost', 'csrf', 'directory', 'limit', 'multipart', 'staticCache', ].forEach(function (name) { // 每次嘗試在express上訪問這些屬性將會報錯 Object.defineProperty(exports, name, { get: function () { throw new Error('Most middleware (like ' + name + ') is no longer bundled with Express and must be installed separately. Please see https://github.com/senchalabs/connect#middleware.'); }, configurable: true }); });
對原有的中間件屬性訪問將會報錯……但是問題是內部又引入了body-parser模塊,看來還是不能完全脫離,偷偷用了一個。
完結。