欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

nodejs模塊學(xué)習(xí)之connect解析

 更新時(shí)間:2017年07月05日 08:25:45   作者:htoooth  
這篇文章主要介紹了nodejs模塊學(xué)習(xí)之connect解析,小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過來看看吧

nodejs 發(fā)展很快,從 npm 上面的包托管數(shù)量就可以看出來。不過從另一方面來看,也是反映了 nodejs 的基礎(chǔ)不穩(wěn)固,需要開發(fā)者創(chuàng)造大量的輪子來解決現(xiàn)實(shí)的問題。

知其然,并知其所以然這是程序員的天性。所以把常用的模塊拿出來看看,看看高手怎么寫的,學(xué)習(xí)其想法,讓自己的技術(shù)能更近一步。

引言

express 是 nodejs 中最流行的 web 框架。express 中對(duì) http 中的 request 和 response 的處理,還有以中間件為核心的處理流程,非常靈活,足以應(yīng)對(duì)任何業(yè)務(wù)的需求。

而 connect 曾經(jīng)是 express 3.x 之前的核心,而 express 4.x 已經(jīng)把 connect 移除,在 express 中自己實(shí)現(xiàn)了 connect 的接口??梢哉f connect 造就了 express 的靈活性。

因此,我很好奇,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); 

從示例中可以看到一個(gè)典型的 connect 的使用:

 var  app = connect() // 初始化 

 app.use( function (req, res, next) { 

    // do something 

 })

 // http 服務(wù)器,使用 

 http.createServer(app).listen(3000); 

先倒著看,從調(diào)用的地方更能看出來,模塊怎么使用的。我們就先從  http.createServer(app)  來看看。

nodejs doc 的官方文檔中可以知,  createServer  函數(shù)的參數(shù)是一個(gè)回調(diào)函數(shù),這個(gè)回調(diào)函數(shù)是用來響應(yīng)  request  事件的。從這里看出,示例代碼中  app  中函數(shù)簽就是  (req, res) ,也就是說  app  的接口為  function (req, res) 。

但是從示例代碼中,我們也可以看出  app  還有一個(gè)  use  方法。是不是覺得很奇怪,js 中函數(shù)實(shí)例上,還以帶方法,這在 js 中就叫 函數(shù)對(duì)象,不僅能調(diào)用,還可以帶實(shí)例變量。給個(gè)例子可以看得更清楚:

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: [] } 

可以看出:函數(shù)中的實(shí)例函數(shù)中的 this 就是指當(dāng)前的實(shí)例,不會(huì)因?yàn)槟闶褂?apply 進(jìn)行環(huán)境改變。

其他就跟對(duì)象沒有什么區(qū)別。

再次回到示例代碼,因該可以看懂了,  connect  方法返回了一個(gè)函數(shù),這個(gè)函數(shù)能直接調(diào)用,有 use 方法,用來響應(yīng) http 的 request 事件。

到此為此,示例代碼就講完了。 我們開始進(jìn)入到 connect 模塊的內(nèi)部。

connect 只有一個(gè)導(dǎo)出方法。就是如下:

 var  merge = require( 'utils-merge' ); 

 module.exports = createServer; 

 var  proto = {}; 

 function  createServer() { 

   // 函數(shù)對(duì)象,這個(gè)對(duì)象能調(diào)用,能加屬性 

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

   merge(app, proto);  // ===等于調(diào)用 Object.assign 

   merge(app, EventEmitter.prototype);  // === 等于調(diào)用 Object.assign 

   app.route =  '/' ; 

   app.stack = []; 

   return  app; 

 } 

從代碼中可以看出,createServer 函數(shù)把 app 函數(shù)返回了,app 函數(shù)有三個(gè)參數(shù),多了一個(gè) next (這個(gè)后面講),app函數(shù)把 proto 的方法合并了。還有 EventEmitter 的方法也合并了,還增加了 route 和 stack 的屬性。

從前面代碼來看,響應(yīng) request 的事件的函數(shù),是 app.handle 方法。這個(gè)方法如下:

 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 ;  // 已經(jīng)拿掉 

    } 

 

    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);  // 沒有中間件,調(diào)用 finalhandler 進(jìn)行處理,如果 err 有值,就返回 404 進(jìn)行處理 

     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);  // 執(zhí)行下一個(gè) 

    } 

 

    // skip if route match does not border "/", ".", or end 

    var  c = path[route.length]; 

    if  (c !== undefined && '/ ' !== c && ' . ' !== c) { 

     return next(err); // 執(zhí)行下一個(gè) 

    } 

 

    // 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(); 

 }; 

代碼中有相應(yīng)的注釋,可以看出,next 方法就是一個(gè)遞歸調(diào)用,不斷的對(duì)比 route 是否匹配,如果匹配則調(diào)用 handle, 如果不匹配,則調(diào)用下一個(gè) handle.

call 函數(shù)的代碼如下:

 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); 

 } 



可以看出一個(gè)重點(diǎn):對(duì)錯(cuò)誤處理,connect 的要求 是函數(shù)必須是 四個(gè)參數(shù),而 express 也是如此。如果有錯(cuò)誤, 中間件沒有一個(gè)參數(shù)的個(gè)數(shù)是 4, 就會(huì)錯(cuò)誤一直傳下去,直到后面的  defer(done, err);  進(jìn)行處理。

還有 app.use 添加中間件:

 proto.use =  function  use(route, fn) { 

   var  handle = fn;  // fn 只是一個(gè)函數(shù)的話 三種接口 // 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' ) {  // 自定義中的函數(shù)對(duì)象 

    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) // 最后的函數(shù) 

   } 

 

   // 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) 這兩種都可以。還有對(duì) fn 情況進(jìn)行特殊處理。

總的處理流程就是這樣,用 use 方法添加中間件,用 next 編歷中間件,用 finalHandle 進(jìn)行最后的處理工作。

在代碼中還有一個(gè)函數(shù)非常奇怪:

 /* istanbul ignore next */ 

 var  defer =  typeof  setImmediate ===  'function' 

   ? setImmediate 

   :  function (fn){ process.nextTick(fn.bind.apply(fn, arguments)) } 



defer  函數(shù)中的  fn.bind.apply(fn, arguments) ,這個(gè)方法主要解決了,一個(gè)問題,不定參的情況下,第一個(gè)參數(shù)函數(shù),怎樣拿到的問題,為什么這樣說呢?如果中我們要達(dá)到以上的效果,需要多多少行代碼?

 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)) } 

總結(jié)

connect 做為 http 中間件模塊,很好地解決對(duì) http 請(qǐng)求的插件化處理的需求,把中間件組織成請(qǐng)求上的一個(gè)處理器,挨個(gè)調(diào)用中間件對(duì) http 請(qǐng)求進(jìn)行處理。

其中 connect 的遞歸調(diào)用,和對(duì) js 的函數(shù)對(duì)象的使用,讓值得學(xué)習(xí),如果讓我來寫,就第一個(gè)調(diào)個(gè)的地方,就想不到使用 函數(shù)對(duì)象 來進(jìn)行處理。

而且 next 的設(shè)計(jì)如此精妙,整個(gè)框架的使用和概念上,對(duì)程序員基本上沒有認(rèn)知負(fù)擔(dān),這才是最重要的地方。這也是為什么 express 框架最受歡迎。koa 相比之下,多幾個(gè)概念,還使用了不常用的 yield 方法。

connect 的設(shè)計(jì)理念可以用在,類似 http 請(qǐng)求模式上, 如 rpc, tcp 處理等。

我把 connect 的設(shè)計(jì)方法叫做 中間件模式,對(duì)處理 流式模式,會(huì)有較好的效果。

以上就是本文的全部內(nèi)容,希望對(duì)大家的學(xué)習(xí)有所幫助,也希望大家多多支持腳本之家。

相關(guān)文章

  • Node.js使用cookie保持登錄的方法

    Node.js使用cookie保持登錄的方法

    本篇文章主要介紹了Node.js使用cookie保持登錄的方法,小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過來看看吧
    2018-05-05
  • Node.js Sequelize如何實(shí)現(xiàn)數(shù)據(jù)庫的讀寫分離

    Node.js Sequelize如何實(shí)現(xiàn)數(shù)據(jù)庫的讀寫分離

    Sequelize是一個(gè)易于使用,支持多SQL方言(dialect)的對(duì)象-關(guān)系映射框架(ORM),這個(gè)庫完全采用JavaScript開發(fā)并且能夠用在Node.JS環(huán)境中。它當(dāng)前支持MySQL, MariaDB, SQLite 和 PostgreSQL 數(shù)據(jù)庫。在Node.js中,使用 Sequelize操作數(shù)據(jù)庫時(shí),同樣支持讀寫分離。
    2016-10-10
  • 基于Node.js實(shí)現(xiàn)一鍵生成個(gè)性化二維碼

    基于Node.js實(shí)現(xiàn)一鍵生成個(gè)性化二維碼

    這篇文章主要為大家詳細(xì)介紹了如何使用Node.js、Jimp和QRCode庫,結(jié)合一個(gè)簡單的腳本,通過命令行命令來快速給二維碼加上指定的背景,打造更有個(gè)性化的二維碼,感興趣的可以了解下
    2024-03-03
  • node實(shí)現(xiàn)批量上傳本地圖片轉(zhuǎn)為圖片CDN的項(xiàng)目實(shí)踐

    node實(shí)現(xiàn)批量上傳本地圖片轉(zhuǎn)為圖片CDN的項(xiàng)目實(shí)踐

    本文主要介紹了node實(shí)現(xiàn)批量上傳本地圖片轉(zhuǎn)為圖片CDN的項(xiàng)目實(shí)踐,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2023-07-07
  • 手把手教你更優(yōu)雅的修改node_modules里的代碼

    手把手教你更優(yōu)雅的修改node_modules里的代碼

    這篇文章主要給大家介紹了關(guān)于如何更優(yōu)雅的修改node_modules里的代碼的相關(guān)資料,文中通過實(shí)例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下
    2023-02-02
  • Node.js命令行/批處理中如何更改Linux用戶密碼淺析

    Node.js命令行/批處理中如何更改Linux用戶密碼淺析

    這篇文章主要給大家介紹了關(guān)于Node.js命令行/批處理中如何更改Linux用戶密碼的相關(guān)資料,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2018-07-07
  • 詳解Node.js中的事件機(jī)制

    詳解Node.js中的事件機(jī)制

    Node.js能夠在眾多的后端JavaScript技術(shù)之中脫穎而出,正是因其基于事件的特點(diǎn)而受到歡迎,所以這篇文章小編給大家詳細(xì)介紹了Node.js中的事件機(jī)制,本文介紹的很詳細(xì),對(duì)大家的理解和學(xué)習(xí)很有幫助,下面來一起看看吧。
    2016-09-09
  • Nodejs連接mysql并實(shí)現(xiàn)增、刪、改、查操作的方法詳解

    Nodejs連接mysql并實(shí)現(xiàn)增、刪、改、查操作的方法詳解

    這篇文章主要介紹了Nodejs連接mysql并實(shí)現(xiàn)增、刪、改、查操作的方法,結(jié)合實(shí)例形式詳細(xì)分析了nodejs針對(duì)mysql數(shù)據(jù)庫的的連接、mysql數(shù)據(jù)庫的創(chuàng)建及nodejs針對(duì)mysql增刪改查等相關(guān)操作具體實(shí)現(xiàn)技巧,需要的朋友可以參考下
    2018-01-01
  • Node.js微信 access_token ( jsapi_ticket ) 存取與刷新的示例

    Node.js微信 access_token ( jsapi_ticket ) 存取與刷新的示例

    本篇文章主要介紹了Node.js微信 access_token ( jsapi_ticket ) 存取與刷新的示例,具有一定的參考價(jià)值,有興趣的可以了解一一下‘
    2017-09-09
  • 爬蟲利器Puppeteer實(shí)戰(zhàn)

    爬蟲利器Puppeteer實(shí)戰(zhàn)

    本文詳細(xì)的介紹了什么是Puppeteer以及使用,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2019-01-01

最新評(píng)論