原生nodejs 學習筆記1


網上許多nodejs教程或書藉都是教你調用第三方模塊來編寫nodejs應用的,雖然這是非常便捷的,但是封裝太厚,你基本一點東西還是沒有學到。人家的模塊,人家想怎么改就行,可以下一版本就改了接口,你的應用就完蛋了。比如說google,他就愛干這種事情。因此我們還得老老實實學習底層API吧。

本節首先教大家跑起一個頁面吧。

我在以前就寫一篇相關的, node.js 一個簡單的頁面輸出,大家可以先預習一下。

一般來說,大家都是從這樣一個例子入門

var http = require("http");
http.createServer(function(request, response) {
  response.writeHead(200, {"Content-Type": "text/plain"});
  response.write("Hello node.js");
  response.end();
}).listen(8888);

上面腳本寫在一個app.js上面,然后打開控制台,定位於它所在目錄,輸入node app,再打開某一瀏覽器,輸入http://localhost:8888/就看出效果。

分析上面腳本:

  1. 引入依賴,這是前端AMD規范未流行前, 非常神奇的東西。不過一個項目這么大,肯定要分成N個目錄N個文件,各人負責一部分,減少合並沖突的風險
  2. require("http")吐出的http對象是一個原生對象,類似於前端的alert, setTimeout,非常常用。而這個http對象更是后端的中流砥柱,WEB應用離不開它
  3. http.createServer方法要接收一個函數,俗稱回調。以后你會看到越來越多回調,nodejs的世界就是回調的世界,nodejs的發展史就是跟回調的抗爭史
  4. 這個回調傳給你兩個重要對象,請求對象與響應對象。response要往前端輸出東西,我們需要設置響應頭(response.writeHead),告訴瀏覽器接着下來要怎么處理我們的內容。因為有的東西可能會當成script腳本,有的當成圖片,有的當成CSS文件,有的要當成附件下載……response.write就是用來輸出內容。輸出結束了要調用end方法,這其實是方便response執行end回調。
  5. http.createServer 其實是返回一個Server實例,它有一個listen方法,它是用於監聽某一端口。

ok,這樣就完了。我們深入一點吧(這里比較難,可以跳過,直接看下一個★★★)。

我們打開這里,查看http模塊的源碼,它大抵調用了

_http_incoming
_http_outgoing
_http_server
_http_client

這四個重要的內部模塊,它們是我們在nodejs環境中訪問不到。http還引用其他內部模塊,但現在可以不理會它們。_http_incoming,_http_outgoing是提供兩個輸入輸出對象,流是以后我們重點學習的東西。在本模塊中,除去哪些已經廢棄的方法,主要是這三個方法:get, request , createServer

我將http源碼刪減一下,大家就明白什么回事了:


//------------------Server------------------
var server = require('_http_server');
exports.ServerResponse = server.ServerResponse;
var Server = exports.Server = server.Server;

exports.createServer = function(requestListener) {
  return new Server(requestListener);
};

//------------------Client------------------
var client = require('_http_client');
var ClientRequest = exports.ClientRequest = client.ClientRequest;

exports.request = function(options, cb) {
  return new ClientRequest(options, cb);
};

exports.get = function(options, cb) { //get是request方法的包裝
  var req = exports.request(options, cb);
  req.end();
  return req;
};

那么requestListener是怎么傳進去的呢?我們到_http_server內部模塊去,發現ServerResponse只是OutgoingMessage的子類,Server實例是通過request事件來綁定我們的createServer回調,同時它也通過connection事件綁定一個connectionListener的方法,connectionListener巨長,里面會emit request事件。

    if (!util.isUndefined(req.headers.expect) &&
        (req.httpVersionMajor == 1 && req.httpVersionMinor == 1) &&
        continueExpression.test(req.headers['expect'])) {
      res._expect_continue = true;
      if (EventEmitter.listenerCount(self, 'checkContinue') > 0) {
        self.emit('checkContinue', req, res);
      } else {
        res.writeContinue();
        self.emit('request', req, res);
      }
    } else {
      self.emit('request', req, res);
    }

res是ServerResponse的實例var res = new ServerResponse(req);,req是parserOnIncoming方法傳進來的,而parserOnIncoming則是 parser.onIncoming 的一個方法. 追蹤到_http_common內部模塊,發現以下幾句:


   parser.onIncoming(parser.incoming, info.shouldKeepAlive)//毫無疑問,parser.incoming就是我們的req對象

   parser.incoming = new IncomingMessage(parser.socket);

好了,一切真相大白,req是IncomingMessage的實例, res是ServerResponse亦即OutgoingMessage的實例。這里不得不吐槽,nodejs的源碼太混亂了!

★★★我們又切換為簡單模式。明白以上那段話,我們就知道我們為什么需要重點學習“http.ServerResponse”與“http.IncomingMessage”,如果大家看過express的源碼,就會發現其request模塊與response模塊就在這上面進行擴展的。

//https://github.com/strongloop/express/blob/master/lib/request.js
var req = exports = module.exports = {
  __proto__: http.IncomingMessage.prototype
};
//https://github.com/strongloop/express/blob/master/lib/response.js
var res = module.exports = {
  __proto__: http.ServerResponse.prototype
};

在好久之前,我們是通過fs模塊的 fs.readFile 來讀取服務器上的某個文件返回給前端:


var http = require("http");
var fs = require('fs');
exports.start = function(){
    http.createServer(function(request, response) {
        fs.readFile('./index.html', 'utf-8',function (err, data) {//讀取內容
            if (err) throw err;
            response.writeHead(200, {"Content-Type": "text/html"});//注意這里
            response.write(data);
            response.end();
        });
    }).listen(8888);
    console.log("server start...");
}

前面已經說過,現在已經是流的時代了,response是一個可寫流(對應可讀流),我們創建一個可讀流就行了。

var http = require("http");
var fs = require("fs")
http.createServer(function (request, response) {
    var readable = fs.createReadStream("./index.html")
    response.writeHead(200, {"Content-Type": "text/html"});
    readable.pipe(response);

}).listen(8888);

如果發生錯誤想跳轉到404頁面

var http = require("http");
var fs = require("fs")
http.createServer(function (request, response) {
    var readable = fs.createReadStream("./index.html")
    response.writeHead(200, {"Content-Type": "text/html"});
    readable.pipe(response);
    readable.on("error", function () {
        var readable = fs.createReadStream("./404.html")
        readable.pipe(response);
    })
}).listen(8888);


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM