您好,登錄后才能下訂單哦!
本篇文章給大家分享的是有關nodejs中connect的作用是什么,小編覺得挺實用的,因此分享給大家學習,希望大家閱讀完這篇文章后可以有所收獲,話不多說,跟著小編一起來看看吧。
connect 解析
我們要先從 connect 的官方例子開始
var connect = require( 'connect' ); var http = require( 'http' ); var app = connect(); // gzip/deflate outgoing responses var compression = require( 'compression' ); app.use(compression()); // store session state in browser cookie var cookieSession = require( 'cookie-session' ); app.use(cookieSession({ keys: [ 'secret1' , 'secret2' ] })); // parse urlencoded request bodies into req.body var bodyParser = require( 'body-parser' ); app.use(bodyParser.urlencoded({extended: false })); // respond to all requests app.use( function (req, res){ res.end( 'Hello from Connect!\n' ); }); //create node.js http server and listen on port http.createServer(app).listen(3000);
從示例中可以看到一個典型的 connect 的使用:
var app = connect() // 初始化 app.use( function (req, res, next) { // do something }) // http 服務器,使用 http.createServer(app).listen(3000);
先倒著看,從調用的地方更能看出來,模塊怎么使用的。我們就先從 http.createServer(app) 來看看。
從 nodejs doc 的官方文檔中可以知, createServer 函數的參數是一個回調函數,這個回調函數是用來響應 request 事件的。從這里看出,示例代碼中 app 中函數簽就是 (req, res) ,也就是說 app 的接口為 function (req, res) 。
但是從示例代碼中,我們也可以看出 app 還有一個 use 方法。是不是覺得很奇怪,js 中函數實例上,還以帶方法,這在 js 中就叫 函數對象,不僅能調用,還可以帶實例變量。給個例子可以看得更清楚:
function handle () { function app(req, res, next) { app.handle(req, res, next)} app.handle = function (req, res, next) { console.log( this ); } app.statck = []; return app; } var app = handle(); app() // ==> { [Function: app] handle: [Function], stack: [] } app.apply({}) // ==>{ [Function: app] handle: [Function], stack: [] }
可以看出:函數中的實例函數中的 this 就是指當前的實例,不會因為你使用 apply 進行環境改變。
其他就跟對象沒有什么區別。
再次回到示例代碼,因該可以看懂了, connect 方法返回了一個函數,這個函數能直接調用,有 use 方法,用來響應 http 的 request 事件。
到此為此,示例代碼就講完了。 我們開始進入到 connect 模塊的內部。
connect 只有一個導出方法。就是如下:
var merge = require( 'utils-merge' ); module.exports = createServer; var proto = {}; function createServer() { // 函數對象,這個對象能調用,能加屬性 function app(req, res, next){ app.handle(req, res, next); } merge(app, proto); // ===等于調用 Object.assign merge(app, EventEmitter.prototype); // === 等于調用 Object.assign app.route = '/' ; app.stack = []; return app; }
從代碼中可以看出,createServer 函數把 app 函數返回了,app 函數有三個參數,多了一個 next (這個后面講),app函數把 proto 的方法合并了。還有 EventEmitter 的方法也合并了,還增加了 route 和 stack 的屬性。
從前面代碼來看,響應 request 的事件的函數,是 app.handle 方法。這個方法如下:
proto.handle = function handle(req, res, out) { var index = 0; var protohost = getProtohost(req.url) || '' ; //獲得 http://www.baidu.com var removed = '' ; var slashAdded = false ; var stack = this .stack; // final function handler var done = out || finalhandler(req, res, { env: env, onerror: logerror }); // 接口 done(err); // store the original URL req.originalUrl = req.originalUrl || req.url; function next(err) { if (slashAdded) { req.url = req.url.substr(1); // 除掉 / 之后的字符串 slashAdded = false ; // 已經拿掉 } if (removed.length !== 0) { req.url = protohost + removed + req.url.substr(protohost.length); removed = '' ; } // next callback var layer = stack[index++]; // all done if (!layer) { defer(done, err); // 沒有中間件,調用 finalhandler 進行處理,如果 err 有值,就返回 404 進行處理 return ; } // route data var path = parseUrl(req).pathname || '/' ; var route = layer.route; // skip this layer if the route doesn't match if (path.toLowerCase().substr(0, route.length) !== route.toLowerCase()) { return next(err); // 執行下一個 } // skip if route match does not border "/", ".", or end var c = path[route.length]; if (c !== undefined && '/ ' !== c && ' . ' !== c) { return next(err); // 執行下一個 } // trim off the part of the url that matches the route if (route.length !== 0 && route !== ' / ') { removed = route; req.url = protohost + req.url.substr(protohost.length + removed.length); // ensure leading slash if (!protohost && req.url[0] !== ' / ') { req.url = ' /' + req.url; slashAdded = true ; } } // call the layer handle call(layer.handle, route, err, req, res, next); } next(); };
代碼中有相應的注釋,可以看出,next 方法就是一個遞歸調用,不斷的對比 route 是否匹配,如果匹配則調用 handle, 如果不匹配,則調用下一個 handle.
call 函數的代碼如下:
function call(handle, route, err, req, res, next) { var arity = handle.length; var error = err; var hasError = Boolean(err); debug( '%s %s : %s' , handle.name || '<anonymous>' , route, req.originalUrl); try { if (hasError && arity === 4) { // error-handling middleware handle(err, req, res, next); return ; } else if (!hasError && arity < 4) { // request-handling middleware handle(req, res, next); return ; } } catch (e) { // replace the error error = e; } // continue next(error); }
可以看出一個重點:對錯誤處理,connect 的要求 是函數必須是 四個參數,而 express 也是如此。如果有錯誤, 中間件沒有一個參數的個數是 4, 就會錯誤一直傳下去,直到后面的 defer(done, err); 進行處理。
還有 app.use 添加中間件:
proto.use = function use(route, fn) { var handle = fn; // fn 只是一個函數的話 三種接口 // 1. err, req, res, next 2. req, res, 3, req, res, next var path = route; // default route to '/' if ( typeof route !== 'string' ) { handle = route; path = '/' ; } // wrap sub-apps if ( typeof handle.handle === 'function' ) { // 自定義中的函數對象 var server = handle; server.route = path; handle = function (req, res, next) { // req, res, next 中間件 server.handle(req, res, next); }; } // wrap vanilla http.Servers if (handle instanceof http.Server) { handle = handle.listeners( 'request' )[0]; // (req, res) // 最后的函數 } // strip trailing slash if (path[path.length - 1] === '/' ) { path = path.slice(0, -1); } // add the middleware debug( 'use %s %s' , path || '/' , handle.name || 'anonymous' ); this .stack.push({ route: path, handle: handle }); return this ; };
從代碼中,可以看出,use 方法添加中間件到 this.stack 中,其中 fn 中間件的形式有兩種: function (req, res, next) 和 handle.handle(req, res, next) 這兩種都可以。還有對 fn 情況進行特殊處理。
總的處理流程就是這樣,用 use 方法添加中間件,用 next 編歷中間件,用 finalHandle 進行最后的處理工作。
在代碼中還有一個函數非常奇怪:
/* istanbul ignore next */ var defer = typeof setImmediate === 'function' ? setImmediate : function (fn){ process.nextTick(fn.bind.apply(fn, arguments)) }
defer 函數中的 fn.bind.apply(fn, arguments) ,這個方法主要解決了,一個問題,不定參的情況下,第一個參數函數,怎樣拿到的問題,為什么這樣說呢?如果中我們要達到以上的效果,需要多多少行代碼?
function () { var cb = Array.from(arguments)[0]; var args = Array.from(arguments).splice(1); process.nextTick( function () { cb.apply( null ,args); }) }
這還是 connect 兼容以前的 es5 之類的方法。如果在 es6 下面,方法可以再次簡化
function (..args){ process.nextTick(fn.bind(...args)) }
總結
connect 做為 http 中間件模塊,很好地解決對 http 請求的插件化處理的需求,把中間件組織成請求上的一個處理器,挨個調用中間件對 http 請求進行處理。
其中 connect 的遞歸調用,和對 js 的函數對象的使用,讓值得學習,如果讓我來寫,就第一個調個的地方,就想不到使用 函數對象 來進行處理。
以上就是nodejs中connect的作用是什么,小編相信有部分知識點可能是我們日常工作會見到或用到的。希望你能通過這篇文章學到更多知識。更多詳情敬請關注億速云行業資訊頻道。
免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。