Node.js學習筆記(二)—— 模組化,node.js學習筆記

來源:互聯網
上載者:User

Node.js學習筆記(二)—— 模組化,node.js學習筆記

歡迎轉載,但請註明出處:http://blog.csdn.net/sysuzjz/article/details/43987289

鳴謝:nodebeginner.org

一個應用由不同模組組成,現在我們就將這幾個模組一一道來。

伺服器模組上一節中,我們用了一個使用Node的例子:
var http = require("http");http.createServer(function(request, response) {  response.writeHead(200, {"Content-Type": "text/plain"});  response.write("Hello World");  response.end();}).listen(8888);
這段代碼用於開啟一個伺服器。http是一個內建的模組,第一行代碼使得本地變數http賦值為http模組對象。這也是最核心的模組。JavaScript有一個特定,就是一切皆對象,函數作為對象的一種,也可以作為函數的參數。我們把其中的匿名回呼函數替換成實名函數。這樣,我們就可以把上面那個例子的耦合度再度降低。
var http = require("http");function onRequest(request, response) {    response.writeHead(200, {"Content-Type": "text/plain"});    response.write("Hello World");    response.end();}http.createServer(onRequest).listen(8080);
把這段代碼放到server.js裡,運行node server.js,結果和之前是一模一樣的。再進一步處理,我們把函數進一步封裝,把伺服器開啟封裝成一個函數。
var http = require("http");function start() {    function onRequest(request, response) {        console.log("Request received.");        response.writeHead(200, {"Content-Type": "text/plain"});        response.write("Hello World");        response.end();    }    http.createServer(onRequest).listen(8888);}start();

有一點值得注意的是,大部分伺服器在訪問http://localhost:8888的時候,會順便訪問http://localhost:8888/favicon.ico。所以,回呼函數可能會被執行兩次。

全域模組上述伺服器模組功能有限,或者說,我們刻意對上述模組進行限制,使其只負責HTTP伺服器方面,而不涉及具體業務處理。為了讓各模組各自獨立,降低耦合度,我們應該設定一個全域的模組,用來各模組之間通訊協同。我們把它命名為index.js,內容如下:
var server = require("./server");server.start();
第一句表示包含了本目錄裡的server.js裡的模組。這樣,就只需要通過運行index.js,就能達到我們之前的效果。但是,我們還得先對server.js作點修改來適應這種變化。
var http = require("http");function start() {    function onRequest(request, response) {        console.log("Request received.");        response.writeHead(200, {"Content-Type": "text/plain"});        response.write("Hello World");        response.end();    }    http.createServer(onRequest).listen(8888);}exports.start = start;
我們可以看到,其實變化的就只有最後一句,exports是本檔案,或者說,本模組的返回對象,給exports對象添加start方法。這是因為start函數是不能被其他模組直接引用的,但是模組返回對象可以通過require方法暴露在其他模組裡。這樣,就達到了在全域模組調用其他模組的目的。
我們運行node index.js,會發現,結果和上面的還是一模一樣。路由模組一般情況下,我們的應用會有多種業務需求,如何通過url,來識別不同請求呢?這就是路由。不像PHP,PHP不負責伺服器部分,所以PHP可以一個檔案負責一個業務,請求也是PHP的檔案名稱,例如action="./index.php"。但Node不同,伺服器也是由Node來搭建,而且集中在server.js中。因此,我們引入了路由機制。即所有同域下的請求,根據路徑來轉交給不同的處理函數,這種做法類似於路由器。我們來介紹兩個新的模組:url和querystring,它們負責url的解析。同http模組類似,它們也是內建的模組,所以我們不需要去安裝它們。我們以一個url為例,介紹下各部分和這兩個模組的對應關係
接下來我們來編寫路由模組。編寫router.js檔案:
function route(pathname) {    console.log("About to route a request for " + pathname);}exports.route = route;
將路由穿插進伺服器模組中。這是因為url來自於createServer回呼函數裡的request對象。
var http = require("http");var url = require("url");function start() {    function onRequest(request, response) {        var pathname = url.parse(request.url).pathname;        console.log("Request for " + pathname + " received.");        response.writeHead(200, {"Content-Type": "text/plain"});        response.write("Hello World");        response.end();    }    http.createServer(onRequest).listen(8888);}exports.start = start;
怎麼將兩者協同起來呢?這就要靠全域模組了。我們先將server.js修改一下,
var http = require("http");var url = require("url");function start(route) {    function onRequest(request, response) {        var pathname = url.parse(request.url).pathname;        route(pathname);        response.writeHead(200, {"Content-Type": "text/plain"});        response.write("Hello World");        response.end();    }    http.createServer(onRequest).listen(8888);}
注意,start函數多出了一個參數,而這個參數在onRequest函數中得到調用,說明這是一個函數,並且參數為路徑字串。而這個函數,其實就是路由模組的返回對象。我們修改下index.js:
var server = require("./server");var router = require("./router");server.start(router.route);
這樣,我們又加入了路由模組。並且,伺服器依舊只幹伺服器的活。當然,如果你覺得url的解析不應該放在伺服器模組的話,你也可以把request對象當做參數傳給route函數,然後在router模組中進行解析。業務處理模組我們雖然加入了路由模組,但實際上,它什麼都沒做。所以,我們還需要一個來做事情的模組,那就是業務處理模組。路由模組根據請求url來決定,交由哪個業務處理函數來執行。假設我們有start和upload兩個業務。編寫handler.js:
function start() {    console.log("Request handler 'start' was called.");}function upload() {    console.log("Request handler 'upload' was called.");}exports.start = start;exports.upload = upload;
可能有些強迫症患者已經發現,如果業務較多的話,下面的exports.xx = xx會異常的多,這對於一個強迫症患者來說簡直不能忍,也給維護帶來了困難。我們封裝一下:
function start() {    console.log("Request handler 'start' was called.");}function upload() {    console.log("Request handler 'upload' was called.");}var exportObj = {    start: start,    upload: upload};exports = exportObj;
瞬間高大上了。那麼,問題來了,怎麼將業務處理模組併入呢?同樣的,還是得靠全域模組。修改index.js
var server = require("./server");var router = require("./router");var handler = require("./handler");handlers['/'] = handlers['start'];server.start(router.route, handler);
似乎不難理解,唯一的問題是,start函數又多出了一個參數,這個參數並不是伺服器模組所需要的,但卻是路由模組所需要的,而路由模組是在伺服器模組中調用的。所以,我們間接的將業務處理模組,通過伺服器模組,傳遞給路由模組。修改server.js:
function start(route, handle) {    function onRequest(request, response) {        var pathname = url.parse(request.url).pathname;        console.log("Request for " + pathname + " received.");        route(handle, pathname);        response.writeHead(200, {"Content-Type": "text/plain"});        response.write("Hello World");        response.end();    }    http.createServer(onRequest).listen(8888);}exports.start = start;
然後修改router.js:
function route(handle, pathname) {    console.log("About to route a request for " + pathname);    if (typeof handle[pathname] === 'function') {        handle[pathname]();    } else {        console.log("No request handler found for " + pathname);    }}exports.route = route;
最終形態這樣似乎就完美了。但還有個小問題,不同業務處理函數也許輸出不一樣,我們總不能在每個業務處理函數中都return要輸出的東西,然後交給伺服器模組去輸出吧?畢竟,輸出靠的是createServer回呼函數的response對象。更好的辦法是,將response對象傳給業務處理模組。那麼,server.js的最終形態就是:
var http = require("http");var url = require("url");function start(route, handler) {    function onRequest(request, response) {        var pathname = url.parse(request.url).pathname;        route(pathname, handler, response);        response.writeHead(200, {"Content-type": "text/plain"});        response.write("hello world");        response.end();    }    http.createServer(onRequest).listen(8080);}exports.start = start;
而路由模組也相應的做點更改(其實就是傳個參數,醬油了一次),router.js最終形態如下:
function route(handle, pathname, response) {    console.log("About to route a request for " + pathname);    if (typeof handle[pathname] === 'function') {        handle[pathname](response);    } else {        console.log("No request handler found for " + pathname);    }}exports.route = route;
業務處理模組handler.js最終如下:
function start(response) {    response.writeHead(200, {"Content-type": "text/plain"});    response.write("Request handler 'start' was called.");    response.end();}function upload(response) {    response.writeHead(200, {"Content-type": "text/plain"});    response.write("Request handler 'upload' was called.");    response.end();}var exportObj = {    start: start,    upload: upload};exports = exportObj;
可能又會有強迫症患者發現了,部分代碼是可以複用的,我們繼續封裝:
function output(response, message) {    response.writeHead(200, {"Content-type": "text/plain"});    response.write(message);    response.end();}function start(response) {    output(response, "Request handler 'start' was called.");}function upload(response) {    output(response, "Request handler 'upload' was called.");}var exportObj = {    start: start,    upload: upload};exports = exportObj;
這樣就告一段落了。
以上只是一些簡單的模組,並沒有多少實際內容,具體內容還是得根據需要來進行填充。也許這種思路不是最佳的,但也並不是一文不值,至少,它體現了一種功能分離、低耦合的思想。如果大家有更好的建議,或者發現不足、錯誤之處,歡迎在評論裡提出。

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.