新聞中心
隨著 Web 2.0 概念和 Ajax 技術(shù)的流行,JavaScript 作為 Ajax 應(yīng)用開(kāi)發(fā)中必不可少的一部分,已經(jīng)得到了廣泛的流行。開(kāi)發(fā)人員也開(kāi)始逐步的熟悉和掌握 JavaScript,并積累了相關(guān)的開(kāi)發(fā)經(jīng)驗(yàn)。雖然 JavaScript 目前主要用在 Web 應(yīng)用中,以瀏覽器作為運(yùn)行平臺(tái),但是已經(jīng)有相關(guān)的嘗試把 JavaScript 遷移到服務(wù)器端,這其中包括 Aptana 的 Jaxer 等。這種做法與 Google GWT 是異曲同工的。Google GWT 允許開(kāi)發(fā)人員使用 Java 語(yǔ)言來(lái)編寫 Web 前端代碼。這兩種做法的目的都是為了復(fù)用開(kāi)發(fā)人員已經(jīng)掌握的知識(shí)和積累的經(jīng)驗(yàn)。在這點(diǎn)上,node.js 類似于 Jaxer。

專注于為中小企業(yè)提供網(wǎng)站建設(shè)、成都網(wǎng)站設(shè)計(jì)服務(wù),電腦端+手機(jī)端+微信端的三站合一,更高效的管理,為中小企業(yè)南沙免費(fèi)做網(wǎng)站提供優(yōu)質(zhì)的服務(wù)。我們立足成都,凝聚了一批互聯(lián)網(wǎng)行業(yè)人才,有力地推動(dòng)了成百上千家企業(yè)的穩(wěn)健成長(zhǎng),幫助中小企業(yè)通過(guò)網(wǎng)站建設(shè)實(shí)現(xiàn)規(guī)模擴(kuò)充和轉(zhuǎn)變。
簡(jiǎn)單的來(lái)說(shuō),node.js 是一個(gè)允許開(kāi)發(fā)人員使用 JavaScript 語(yǔ)言編寫服務(wù)器端代碼的框架。也就是說(shuō)編寫的 JavaScript 代碼可以直接運(yùn)行在本地機(jī)器上,而不僅限于瀏覽器。從實(shí)現(xiàn)的角度來(lái)說(shuō),Jaxer 和 node.js 都使用了已有的 JavaScript 執(zhí)行引擎。Jaxer 用的是 Mozilla Firefox 中使用的 JavaScript 引擎,而 node.js 用的則是 Google Chrome 中用的 V8 引擎。
node.js 入門
node.js 可以運(yùn)行在 Linux、Windows 和 Macintosh 等主流的操作系統(tǒng)上。在 Windows 平臺(tái)上運(yùn)行 node.js 的話,需要 Cygwin 或是 MinGW 的支持。下面以常用的 Windows 平臺(tái)為例來(lái)說(shuō)明。首先需要安裝 Cygwin。安裝的時(shí)候需要選擇 gcc-g++ 、make、openssl 和 python 等包。gcc 的版本必須是***的。接著從 參考資料 中給出的地址下載 node.js 0.4.0 版本的源代碼。下載解壓之后,依次在 Cygwin 中運(yùn)行 ./configure 、make 和 make install 等命令進(jìn)行編譯和安裝。安裝完成之后,直接運(yùn)行 node 命令就可以啟動(dòng) node.js 提供的命令行。在命令行中可以直接輸入 JavaScript 代碼并運(yùn)行。也可以通過(guò) node server.js 的方式來(lái)運(yùn)行一個(gè) JavaScript 文件 server.js 。
代碼清單 1 中給出了一個(gè)簡(jiǎn)單的“Hello World”程序的示例。通過(guò) node helloworld.js 來(lái)運(yùn)行該 JavaScript 文件之后,會(huì)在控制臺(tái)輸出“Hello World”。
清單 1. 使用 node.js 的“Hello World”程序
- process.stdout.write("Hello World");
代碼清單 1 中的 process 表示的是當(dāng)前運(yùn)行的 node.js 進(jìn)程,其屬性 stdout 表示的是進(jìn)程的標(biāo)準(zhǔn)輸出流。通過(guò) write() 方法向給流中寫入一個(gè)字符串。從 代碼清單 1 可以看到,使用 JavaScript 就可以訪問(wèn)標(biāo)準(zhǔn)輸出流等本地系統(tǒng)上的資源。這從一個(gè)側(cè)面反映出來(lái)了 node.js 的強(qiáng)大。
在 node.js 可以運(yùn)行的 JavaScript 代碼中,可以使用一些全局的對(duì)象:包括 代碼清單 1 中用到的 process 、下面會(huì)介紹的用來(lái)加載模塊的 require() 方法、表示當(dāng)前正在執(zhí)行的 JavaScript 文件名的 __filename 、表示當(dāng)前正在執(zhí)行的 JavaScript 文件的目錄的__dirname 和與瀏覽器中相似的用來(lái)執(zhí)行定時(shí)任務(wù)的 setTimeout() 和 setInterval() 方法等。
在介紹了 node.js 的基本知識(shí)之后,下面介紹 node.js 的模塊化結(jié)構(gòu)。
模塊化結(jié)構(gòu)
node.js 使用了 CommonJS 定義的模塊系統(tǒng)。不同的功能組件被劃分成不同的模塊。應(yīng)用可以根據(jù)自己的需要來(lái)選擇使用合適的模塊。每個(gè)模塊都會(huì)暴露一些公共的方法或?qū)傩?。模塊使用者直接使用這些方法或?qū)傩约纯?,不需要關(guān)系模塊內(nèi)部的實(shí)現(xiàn)細(xì)節(jié)。除了系統(tǒng)預(yù)置的多個(gè)模塊之外,應(yīng)用開(kāi)發(fā)團(tuán)隊(duì)也可以利用這個(gè)機(jī)制來(lái)將應(yīng)用拆分成多個(gè)模塊,以提高代碼的可復(fù)用性。
使用模塊
在 node.js 中使用一個(gè)模塊的方式是非常簡(jiǎn)單的。使用某個(gè)模塊之前需要首先聲明對(duì)它的依賴。在 JavaScript 代碼中可以直接使用全局函數(shù) require() 來(lái)加載一個(gè)模塊。如 require("http") 可以加載系統(tǒng)預(yù)置的 http 模塊。而 require("./myModule.js") 用來(lái)加載與當(dāng)前 JavaScript 文件同一目錄下的 myModule.js 模塊。如果使用 require() 的路徑以“/”開(kāi)頭的話,則認(rèn)為是模塊 JavaScript 文件在操作系統(tǒng)上的絕對(duì)路徑。如果不是這兩種情況的話,node.js 就會(huì)嘗試在當(dāng)前 JavaScript 文件的父目錄及其祖先目錄下的 node_modules目錄下查找。比如目錄 /usr/home/my.js 中調(diào)用了 require("other.js") 的話,node.js 會(huì)依次嘗試查找下列文件:/usr/home/node_modules/other.js 、/usr/node_modules/other.js 和 /node_modules/other.js 。
require() 方法的返回值是該模塊所暴露出來(lái)的公開(kāi) JavaScript 對(duì)象,包含了可供使用的方法和屬性。代碼清單 2 給出了模塊的基本使用方式。
清單 2. 模塊的基本使用方式
- var greetings = require("./greetings.js");
- var msg = greetings.sayHello("Alex", "zh_CN");
- process.stdout.write(msg);
如 代碼清單 2 所示,一般是直接把 require() 方法的返回值賦值給一個(gè)變量,在 JavaScript 代碼中直接使用此變量即可。greetings.js 模塊暴露了一個(gè) sayHello() 方法,當(dāng)前 JavaScript 代碼直接使用了該方法。
開(kāi)發(fā)自己的模塊
開(kāi)發(fā)自己的模塊的基本工作是在模塊對(duì)應(yīng)的 JavaScript 文件中編寫模塊相關(guān)的代碼。這其中封裝了模塊的內(nèi)部處理邏輯。一般來(lái)說(shuō),一個(gè)模塊通常會(huì)暴露一些公開(kāi)的方法或?qū)傩越o其使用者。模塊的內(nèi)部代碼需要把這些方法或?qū)傩越o暴露出來(lái)。代碼清單 3 給出了 代碼清單 2 中所使用的 greetings.js 文件的內(nèi)容。
清單 3. greetings.js 模塊的內(nèi)容
- var languages = {
- "zh_CN" : "你好,",
- "en" : "Hello, "
- };
- exports.sayHello = function(name, language) {
- return languages[language] || languages["en"] + name;
- };
如 代碼清單 3 所示,exports 對(duì)象的內(nèi)容就是模塊的使用者調(diào)用 require() 方法的返回值中所包含的內(nèi)容。模塊通過(guò)這種方式來(lái)聲明其所暴露出來(lái)的公開(kāi)方法和屬性。在模塊中定義的變量,如 languages ,是只對(duì)模塊內(nèi)部的代碼可見(jiàn)的。
如果一個(gè)模塊所包含的內(nèi)容比較多,也可以用文件夾的方式來(lái)組織??梢栽谖募A的根目錄下面創(chuàng)建一個(gè) package.json 文件,其內(nèi)容中包含了模塊的名稱和入口 JavaScript 文件的路徑。如果沒(méi)有提供這個(gè) package.json 文件的話,node.js 會(huì)默認(rèn)在文件夾中查找index.js 文件作為模塊的啟動(dòng) JavaScript 文件。
在介紹完 node.js 的模塊化結(jié)構(gòu)之后,下面介紹其事件驅(qū)動(dòng)機(jī)制。
事件驅(qū)動(dòng)
開(kāi)發(fā)過(guò) Web 應(yīng)用的人都熟悉瀏覽器中的事件處理機(jī)制。當(dāng)對(duì)某個(gè) DOM 元素上的某類事件感興趣的時(shí)候,只需要在該 DOM 元素上面注冊(cè)一個(gè)事件監(jiān)聽(tīng)器即可。如 ele.addEventListener("click", function() {}) 就添加了一個(gè)對(duì) click 事件的監(jiān)聽(tīng)器。當(dāng)事件發(fā)生的時(shí)候,事件監(jiān)聽(tīng)器的 JavaScript 方法就會(huì)被調(diào)用。事件的處理方法是異步執(zhí)行的。這種異步執(zhí)行的方式非常適合于開(kāi)發(fā)高性能并發(fā)網(wǎng)絡(luò)應(yīng)用。實(shí)際上,目前的高性能并發(fā)應(yīng)用開(kāi)發(fā)一般有兩種做法:***種是使用多線程的機(jī)制,另外一種就是采用基于事件驅(qū)動(dòng)的方式。多線程的問(wèn)題在于應(yīng)用開(kāi)發(fā)起來(lái)難度較高,很容易出現(xiàn)線程饑餓或是死鎖等問(wèn)題,對(duì)開(kāi)發(fā)人員提出了更高的要求。而事件驅(qū)動(dòng)的方式則更加靈活,很容易為 Web 開(kāi)發(fā)人員所理解和使用,也不存在線程死鎖等問(wèn)題。依托于性能強(qiáng)大的 Google V8 引擎和先進(jìn)的事件 I/O 架構(gòu),node.js 可以成為創(chuàng)建高性能服務(wù)器端應(yīng)用的良好基礎(chǔ)。
基于 node.js 開(kāi)發(fā)應(yīng)用與開(kāi)發(fā) Web 應(yīng)用有相似的編程模型。很多模塊都會(huì)暴露出一些事件,使用這些模塊的代碼通過(guò)注冊(cè)事件監(jiān)聽(tīng)器的方式來(lái)添加相應(yīng)的處理邏輯。代碼清單 4 中給出了一個(gè)簡(jiǎn)單的 HTTP 代理服務(wù)器的實(shí)現(xiàn)代碼。
清單 4. HTTP 代理服務(wù)器
- var http = require("http");
- var url = require("url");
- http.createServer(function (req, res) {
- var urlObj = url.parse(req.url, true); // 獲取被代理的 URL
- var urlToProxy = urlObj.query.url;
- if (!urlToProxy) {
- res.statusCode = 400;
- res.end("URL 是必須的。");
- }
- else {
- console.log("處理代理請(qǐng)求:" + urlToProxy);
- var parsedUrl = url.parse(urlToProxy);
- var opt = {
- host : parsedUrl.hostname,
- port : parsedUrl.port || 80,
- path : (parsedUrl.pathname || "") + (parsedUrl.search || "")
- + (parsedUrl.hash || "")
- };
- http.get(opt, function(pres) { // 請(qǐng)求被代理 URL 的內(nèi)容
- res.statusCode = pres.statusCode;
- var headers = pres.headers;
- for (var key in headers) {
- res.setHeader(key, headers[key]);
- }
- pres.on("data", function(chunk) {
- res.write(chunk); // 寫回?cái)?shù)據(jù)
- });
- pres.on("end", function() {
- res.end();
- });
- });
- }
- }).listen(8088, "127.0.0.1");
- console.log("代理服務(wù)器已經(jīng)在 8088 端口啟動(dòng)。");
整個(gè)代理服務(wù)器的實(shí)現(xiàn)比較簡(jiǎn)單。首先通過(guò) http 模塊中的 createServer() 方法用來(lái)創(chuàng)建一個(gè) HTTP 服務(wù)器,再通過(guò) listen() 方法就可以讓該 HTTP 服務(wù)器在特定端口監(jiān)聽(tīng)。在 createServer() 方法中傳入的參數(shù)是 HTTP 請(qǐng)求的響應(yīng)方法。實(shí)際上,每個(gè) HTTP 請(qǐng)求都是對(duì)應(yīng)于 HTTP 服務(wù)器上的一個(gè) request 事件。代碼清單 4 中的 HTTP 服務(wù)器創(chuàng)建部分實(shí)際上等價(jià)于 代碼清單 5 中給出的實(shí)現(xiàn)方式。
清單 5. 使用事件機(jī)制的 HTTP 服務(wù)器創(chuàng)建方式
- var server = http.createServer();
- server.on("request", function(req, res) {
- });
在請(qǐng)求的處理方法里面,通過(guò) http.get() 方法來(lái)獲取被代理 URL 的內(nèi)容。這里同樣采用了基于事件的處理方式。pres.on("data", function(chunk) {}) 在 pres 的 data 事件上添加了一個(gè)處理方法。該方法的作用是當(dāng)獲取到被代理 URL 的內(nèi)容的時(shí)候,就把獲取到的內(nèi)容寫回到原始 HTTP 請(qǐng)求的響應(yīng)中。對(duì)于 end 事件的處理也是同樣的。在使用 node.js 進(jìn)行開(kāi)發(fā)的時(shí)候,會(huì)經(jīng)常遇到這種使用事件處理方法和回調(diào)方法的場(chǎng)景。
在介紹了 node.js 的事件驅(qū)動(dòng)機(jī)制之后,下面介紹一些常用的模塊。
#p#
常用模塊
node.js 默認(rèn)提供了很多與網(wǎng)絡(luò)與文件系統(tǒng)操作相關(guān)的模塊。這些模塊是構(gòu)建服務(wù)器端應(yīng)用的基礎(chǔ)。下面對(duì)其中一些常見(jiàn)的模塊進(jìn)行具體說(shuō)明。
事件模塊
前面提到過(guò),node.js 采用的是事件驅(qū)動(dòng)的架構(gòu),其中的很多模塊都會(huì)產(chǎn)生各種不同的事件,可以由模塊使用者來(lái)添加事件處理方法。所有能夠產(chǎn)生事件的對(duì)象都是事件模塊中的 EventEmitter 類的實(shí)例。
EventEmitter 類中的方法都與事件的產(chǎn)生和處理相關(guān),如下所示:
◆ addListener(event, listener) 和 on(event, listener) :這兩個(gè)方法的作用都是用來(lái)為某個(gè)事件 event 添加事件處理方法listener 。
◆ once(event, listener) :這個(gè)方法為某個(gè)事件 event 添加僅執(zhí)行一次的處理方法 listener 。處理方法在執(zhí)行一次之后就會(huì)被刪除。
◆ removeListener(event, listener) :該方法用來(lái)刪除某個(gè)事件 event 上的處理方法 listener 。
◆ emit(event, [arg1], [arg2], [...]) :該方法用來(lái)產(chǎn)生某個(gè)事件 event 。事件名稱 event 之后的參數(shù)被傳遞給對(duì)應(yīng)的事件處理方法。
代碼清單 6 給出了事件模塊的使用示例。
清單 6. 事件模塊的使用示例
- var events = require("events");
- var emitter = new events.EventEmitter();
- emitter.on("myEvent", function(msg) {
- console.log(msg);
- });
- emitter.emit("myEvent", "Hello World.");
在事件模塊中有一個(gè)特殊的事件 error 。當(dāng)出現(xiàn)錯(cuò)誤的時(shí)候,EventEmitter 會(huì)產(chǎn)生此事件。如果此事件沒(méi)有對(duì)應(yīng)的處理方法的話,默認(rèn)的行為是輸出錯(cuò)誤信息后,程序自動(dòng)終止。因此,需要注意總是添加一個(gè)對(duì) error 事件的處理方法。
流
node.js 中存在各式各樣不同的數(shù)據(jù)流,包括文件系統(tǒng)、HTTP 請(qǐng)求和響應(yīng)、以及 TCP/UDP 連接等。這些流都是 EventEmitter 的實(shí)例,因此可以產(chǎn)生各種不同的事件。流可以分成只讀、只寫和讀寫流三種。
可讀流主要會(huì)產(chǎn)生 4 個(gè)事件:
◆ data :當(dāng)讀取到流中的數(shù)據(jù)時(shí),產(chǎn)生此事件。
◆ end :當(dāng)流中沒(méi)有數(shù)據(jù)可讀時(shí),產(chǎn)生此事件。
◆ error :當(dāng)讀取數(shù)據(jù)出現(xiàn)錯(cuò)誤時(shí),產(chǎn)生此事件。
◆ close :當(dāng)流被關(guān)閉時(shí),產(chǎn)生此事件。
除了上面的事件之外,還有一個(gè) pipe() 方法可以用來(lái)把當(dāng)前的可讀流和另外一個(gè)可寫流連接起來(lái)??勺x流中的數(shù)據(jù)會(huì)被自動(dòng)寫入到可寫流中。
可寫流中最常用的是 write() 和 end() 兩個(gè)方法。write() 方法用來(lái)向流中寫入數(shù)據(jù),而 end() 則用來(lái)結(jié)束寫入操作。
為了表示二進(jìn)制數(shù)據(jù),node.js 使用了類 Buffer 來(lái)表示數(shù)據(jù)緩沖區(qū),以對(duì)二進(jìn)制數(shù)據(jù)進(jìn)行操作。Buffer 類內(nèi)部是以數(shù)組的方式來(lái)存儲(chǔ)數(shù)據(jù)的。一旦創(chuàng)建出來(lái)之后,Buffer 的大小是不能被修改的。Buffer 類的實(shí)例是可以與 JavaScript 中的字符串類型互相轉(zhuǎn)換的。在轉(zhuǎn)換的時(shí)候需要指定編碼格式。通過(guò) Buffer 類的 toString(encoding, start, end) 方法可以把 Buffer 中的從 start 到 end 的內(nèi)容轉(zhuǎn)換成以 encoding 編碼的字符串??梢灾С值木幋a格式有:ascii 、utf8 和 base64 。通過(guò) new Buffer(str, encoding) 可以用一個(gè)字符串 str 來(lái)初始化一個(gè)緩沖區(qū)。write(string, offset, encoding) 用來(lái)把一個(gè)字符串 string 以編碼格式 encoding 寫入到緩沖區(qū)中以 offset 開(kāi)始的位置上。
網(wǎng)絡(luò)操作
node.js 提供了一些與網(wǎng)絡(luò)操作相關(guān)的模塊,包括 TCP、UDP 和 HTTP 等,可以實(shí)現(xiàn)網(wǎng)絡(luò)服務(wù)器和客戶端。
與 TCP 協(xié)議相關(guān)的實(shí)現(xiàn)在 net 模塊中。通過(guò)該模塊的 createServer(connectionListener) 方法可以創(chuàng)建一個(gè) TCP 服務(wù)器。參數(shù)connectionListener 是當(dāng)有客戶端連接到該服務(wù)器上時(shí)的處理方法,等價(jià)于對(duì) connect 事件的處理。一個(gè) TCP 服務(wù)器是類 Server 的實(shí)例。通過(guò) listen 方法可以讓服務(wù)器在指定端口監(jiān)聽(tīng)。
如果想連接到一個(gè)已有的 TCP 服務(wù)器的話,可以使用 createConnection(port, host) 方法來(lái)連接到指定主機(jī) host 的端口 port 上。該方法的返回值是 Socket 類的實(shí)例,表示一個(gè)套接字連接。得到一個(gè) Socket 類的實(shí)例之后,就可以通過(guò) write() 方法來(lái)向該連接中寫入數(shù)據(jù)。如果想從該連接上獲取數(shù)據(jù)的話,可以添加 data 事件的處理方法。
代碼清單 7 中給出了一個(gè)簡(jiǎn)單的用來(lái)進(jìn)行表達(dá)式計(jì)算的 TCP 服務(wù)器,可以通過(guò) telnet 命令連接到此服務(wù)器來(lái)進(jìn)行測(cè)試。
清單 7. 簡(jiǎn)單的表達(dá)式計(jì)算服務(wù)器
- var net = require("net");
- var server = net.createServer(function(socket) {
- socket.setEncoding("utf8");
- var buffer = [], len = 0;
- socket.on("data", function(data) { // 接收到客戶端數(shù)據(jù)
- if (data.charCodeAt(0) == 13) {
- var expr = buffer.join("");
- try {
- var result = eval(expr); // 進(jìn)行計(jì)算
- socket.write(result.toString()); // 寫回結(jié)果
- } catch (e) {
- socket.write("Wrong expression.");
- } finally {
- socket.write("\r\n");
- buffer = [];
- }
- }
- else {
- buffer.push(data);
- }
- });
- });
- server.listen(8180, "127.0.0.1");
- console.log("服務(wù)器已經(jīng)在端口 8180 啟動(dòng)。");
除了 TCP 服務(wù)器外,模塊 http 和 https 可以分別實(shí)現(xiàn) HTTP 和 HTTPS 服務(wù)器,模塊 dgram 可以實(shí)現(xiàn) UDP/Datagram 套接字連接,模塊 tls 可以實(shí)現(xiàn)安全的套接字連接(SSL)。這些模塊的使用方式都類似于模塊 tcp 。
文件系統(tǒng)
node.js 中的 fs 模塊用來(lái)對(duì)本地文件系統(tǒng)進(jìn)行操作。fs 模塊中提供的方法可以用來(lái)執(zhí)行基本的文件操作,包括讀、寫、重命名、創(chuàng)建和刪除目錄以及獲取文件元數(shù)據(jù)等。每個(gè)操作文件的方法都有同步和異步兩個(gè)版本。異步操作的版本都會(huì)使用一個(gè)回調(diào)方法作為***一個(gè)參數(shù)。當(dāng)操作完成的時(shí)候,該回調(diào)方法會(huì)被調(diào)用。而回調(diào)方法的***個(gè)參數(shù)總是保留為操作時(shí)可能出現(xiàn)的異常。如果操作正確成功,則***個(gè)參數(shù)的值是 null 或 undefined 。而同步操作的版本的方法名稱則是在對(duì)應(yīng)的異步方法之后加上一個(gè) Sync 作為后綴。比如異步的 rename() 方法的同步版本是 renameSync() 。下面列出來(lái)了 fs 模塊中的一些常用方法,都只介紹異步操作的版本。
◆ rename(path1, path2) :將路徑 path1 表示的目錄或文件重命名成路徑 path2 。
◆ truncate(fd, len) :將文件描述符 fd 對(duì)應(yīng)的文件的長(zhǎng)度截?cái)酁?len 。
◆ chmod(path, mode) :將路徑 path 表示的目錄或文件的權(quán)限修改為 mode 。
◆ stat(path) :獲取路徑 path 表示的目錄或文件的元數(shù)據(jù)。元數(shù)據(jù)用 Stats 類來(lái)表示。
◆ open(path, flags, mode) :打開(kāi)一個(gè)路徑 path 表示的文件?;卣{(diào)方法中可以得到該文件的描述符。
◆ read(fd, buffer, offset, length, position) :讀取給定文件描述符 fd 所表示的文件中從 position 位置開(kāi)始的長(zhǎng)度為length 字節(jié)的數(shù)據(jù),并存放到緩沖區(qū) buffer 中從 offset 起始的位置上?;卣{(diào)方法中可以得到實(shí)際讀取的字節(jié)數(shù)。
◆ write(fd, buffer, offset, length, position) :將緩沖區(qū) buffer 中的數(shù)據(jù)寫入到文件描述符 fd 所表示的文件中。參數(shù)的含義與 read() 方法一樣?;卣{(diào)方法中可以得到實(shí)際寫入的字節(jié)數(shù)。
◆ readFile(filename, encoding) :以編碼格式 encoding 來(lái)讀取一個(gè)文件 filename 中的內(nèi)容。回調(diào)方法中可以得到文件的內(nèi)容。
◆ writeFile(filename, data, encoding) :將數(shù)據(jù) data 以編碼格式 encoding 寫入到文件 filename 中。
除了上面列出來(lái)的直接操作文件本身的方法外,還可以把文件轉(zhuǎn)換成流。createReadStream(path, options) 和createWriteStream(path, options) 分別用來(lái)從文件中創(chuàng)建可讀和可寫流。參數(shù) path 表示的是文件的路徑,options 是一個(gè)表示讀取或?qū)懭胛募r(shí)的選項(xiàng)的 JavaScript 對(duì)象。
代碼清單 8 中給出了一個(gè)簡(jiǎn)單的 HTTP 靜態(tài)文件服務(wù)器的實(shí)現(xiàn)。
清單 8. HTTP 靜態(tài)文件服務(wù)器
- var http = require("http"),
- fs = require("fs"),
- path = require("path"),
- url = require("url");
- var server = http.createServer(function(req, res) {
- var pathname = url.parse(req.url).pathname;
- var filepath = path.join("/tmp", "wwwroot", pathname);
- var stream = fs.createReadStream(filepath, {flags : "r", encoding : null});
- stream.on("error", function() {
- res.writeHead(404);
- res.end();
- });
- stream.pipe(res);
- });
- server.on("error", function(error) {
- console.log(error);
- });
- server.listen(8088, "127.0.0.1");
如代碼清單 8 所示,首先把 HTTP 請(qǐng)求的路徑轉(zhuǎn)換成服務(wù)器上文件路徑,再?gòu)奈募袆?chuàng)建可讀流,***通過(guò) pipe() 方法把文件的數(shù)據(jù)流傳遞到 HTTP 請(qǐng)求的響應(yīng)中。
#p#
輔助模塊
除了上面介紹的這些常見(jiàn)模塊之外,node.js 還提供了一些輔助的模塊。
模塊 path 用來(lái)處理文件系統(tǒng)上的路徑。這個(gè)模塊中的 join() 用來(lái)把多個(gè)路徑連接起來(lái),形成一個(gè)完整的路徑。如 join("/usr", "home", "test/index.html") 的結(jié)果是路徑 /usr/home/test/index.html 。normalize() 用來(lái)對(duì)路徑進(jìn)行歸一化操作,去掉其中多余的“/”以及處理“..”和“.”。resolve([from ...], to) 方法用來(lái)獲取給定路徑 to 的絕對(duì)路徑。如果 to 不是絕對(duì)路徑,就把它之前的參數(shù)按從右到左的順序添加上去,直到得到了一個(gè)絕對(duì)路徑。如果到***還是無(wú)法得到絕對(duì)路徑,就把當(dāng)前的工作目錄加上。假設(shè)當(dāng)前的工作目錄是 /usr/home ,那么 resolve("test", "index.html") 的返回結(jié)果是 /usr/home/test/index.html 。dirname() 方法用來(lái)獲取路徑的目錄部分。如 dirname("/usr/home/index.html") 的返回結(jié)果是 /usr/home 。basename() 用來(lái)獲取路徑的***一個(gè)部分。如basename("/usr/home/index.html") 的返回結(jié)果是 index.html 。extname() 用來(lái)獲取一個(gè)路徑的文件擴(kuò)展名部分。如extname("/usr/home/index.html") 的返回結(jié)果是 .html 。
模塊 url 用來(lái)對(duì) URL 進(jìn)行解析。parse(urlStr, parseQueryString) 方法用來(lái)把一個(gè) URL 字符串 urlStr 解析成主機(jī)名、端口和路徑等幾個(gè)部分。該方法的返回值是一個(gè)包含了 protocol 、hostname 、port 、pathname 和 query 等屬性的 JavaScript 對(duì)象。如果參數(shù) parseQueryString 的值為 true 的話,URL 中包含的查詢字符串部分也會(huì)被解析。format(urlObj) 方法的作用與 parse() 方法正好相反,用來(lái)從一個(gè) JavaScript 對(duì)象中構(gòu)建出 URL 字符串。
模塊 querystring 用來(lái)處理 URL 中的查詢字符串。stringify(obj) 方法用來(lái)把一個(gè) JavaScript 對(duì)象 obj 轉(zhuǎn)換成查詢字符串的格式。如 stringify({a : 1, b : "good"}) 的返回結(jié)果是 a=1&b=good 。parse(str) 用來(lái)把一個(gè)查詢字符串解析成 JavaScript 對(duì)象。
模塊 vm 可以用來(lái)執(zhí)行 JavaScript 代碼。方法 runInThisContext(code) 用來(lái)執(zhí)行一段 JavaScript 代碼 code 并返回其結(jié)果。通過(guò)該方法運(yùn)行的 JavaScript 代碼不能訪問(wèn)當(dāng)前代碼的作用域。runInNewContext(code, [sandbox]) 方法也是用來(lái)執(zhí)行 JavaScript 代碼的,與 runInThisContext() 不同的是通過(guò)該方法運(yùn)行的 JavaScript 代碼使用 sandbox 對(duì)象作為全局對(duì)象。如 runInNewContext("a + 3", {a : 4}) 的返回結(jié)果是 7。createScript(code) 方法用來(lái)預(yù)先編譯一段 JavaScript 代碼,但是并不立即執(zhí)行。該方法的返回值是一個(gè) Script 對(duì)象。該對(duì)象同樣有 runInThisContext() 和 runInNewContext([sandbox]) 兩個(gè)方法,含義與上面提到的兩個(gè)方法類似。
模塊 os 提供了與底層操作系統(tǒng)相關(guān)的一些信息。包括 hostname() 用來(lái)獲取操作系統(tǒng)的主機(jī)名;type() 用來(lái)獲取操作系統(tǒng)的類型;release() 用來(lái)獲取操作系統(tǒng)的發(fā)行版本號(hào);uptime() 用來(lái)獲取以秒計(jì)算的系統(tǒng)運(yùn)行時(shí)間;cpus() 用來(lái)獲取 CPU 的相關(guān)信息。freemem() 和 totalmem() 分別用來(lái)獲取系統(tǒng)的內(nèi)存總數(shù)和可用內(nèi)存數(shù)。
模塊 util 提供了一些常用的輔助方法。debug(string) 方法用來(lái)輸出信息到標(biāo)準(zhǔn)錯(cuò)誤流。log(string) 方法用來(lái)輸出附帶時(shí)間戳的信息到標(biāo)準(zhǔn)輸出流。inspect(object, showHidden, depth) 方法用來(lái)輸出一個(gè)對(duì)象的內(nèi)部結(jié)構(gòu),參數(shù) object 是要查看的對(duì)象,showHidden 表示是否查看對(duì)象的隱藏屬性,depth 表示查看的對(duì)象層次結(jié)構(gòu)的深度,默認(rèn)值是 2。inherits(constructor, superConstructor) 方法用來(lái)實(shí)現(xiàn) JavaScript 中基于原型的繼承機(jī)制。
在介紹完 node.js 提供的常用模塊之后,下面通過(guò)一個(gè)完整的示例來(lái)說(shuō)明 node.js 的用法。
實(shí)例分析
這個(gè)實(shí)例實(shí)現(xiàn)的功能是動(dòng)態(tài)監(jiān)測(cè)服務(wù)器的內(nèi)存使用狀態(tài),即內(nèi)存的占用率。獲取服務(wù)器上的內(nèi)存占用率比較簡(jiǎn)單,只需要使用 os 模塊提供的方法即可,即 freemem()/totalmem() 。為了能夠?qū)崟r(shí)的監(jiān)測(cè)內(nèi)存占有率,服務(wù)器需要實(shí)時(shí)的把數(shù)據(jù)傳輸給瀏覽器端。這里***的實(shí)現(xiàn)方式是 HTML 5 中引入的 WebSocket 規(guī)范。該規(guī)范在 Firefox 4 和 Google Chrome 等新瀏覽器上得到了支持。同時(shí)服務(wù)器端也需要支持此規(guī)范。Socket.IO 在 node.js 上提供了對(duì) WebSocket 規(guī)范的支持,包括服務(wù)器端和瀏覽器端代碼。代碼清單 9 給出了使用 Socket.IO 的服務(wù)器端代碼。
清單 9. 監(jiān)測(cè)內(nèi)存占用率的服務(wù)器端代碼
- var io = require('./socket.io');
- var io = io.listen(server);
- io.on("connection", function(client){
- setInterval(function() {
- client.send(os.freemem() / os.totalmem());
- }, 500);
- });
在 代碼清單 9 中,server 是 node.js 中的一個(gè) HTTP 服務(wù)器對(duì)象,用來(lái)響應(yīng)一般的 HTTP 請(qǐng)求。Socket.IO 可以對(duì) node.js 的 HTTP 服務(wù)器的請(qǐng)求進(jìn)行攔截,將部分請(qǐng)求交給 Socket.IO 來(lái)處理。這里的處理邏輯是當(dāng)有客戶端連接上的時(shí)候,就每隔 500 毫秒把服務(wù)器的內(nèi)存占用率發(fā)送給客戶端。代碼清單 10 給出了瀏覽器端的 HTML 和 JavaScript 代碼。
清單 10. 監(jiān)測(cè)內(nèi)存占用率的瀏覽器端代碼
服務(wù)器內(nèi)存使用情況 內(nèi)存使用情況
如 代碼清單 10 所示,首先建立一個(gè)與服務(wù)器之間的 WebSocket 連接。通過(guò) message 事件定義了當(dāng)接收到服務(wù)器端的消息時(shí),更新瀏覽器端的顯示。瀏覽器端通過(guò)一個(gè) HTML 5 提供的 canvas 來(lái)繪制內(nèi)存占用率的曲線圖,如 圖 1 所示。
圖 1. 內(nèi)存占用率的曲線圖
總結(jié)
一提到服務(wù)器端開(kāi)發(fā),開(kāi)發(fā)人員一般想到的就是Java 和 C/C++ 等語(yǔ)言。但是通過(guò) node.js 提供的強(qiáng)大能力,熟悉 JavaScript 的 Web 開(kāi)發(fā)人員也可以開(kāi)發(fā)出服務(wù)器端的應(yīng)用。本文詳細(xì)介紹了 node.js 的事件驅(qū)動(dòng)機(jī)制和模塊化結(jié)構(gòu),并對(duì)其中的常用模塊做了詳細(xì)說(shuō)明,***通過(guò)一個(gè)完整的實(shí)例展示了 node.js 的實(shí)用性。
原文:http://www.ibm.com/developerworks/cn/web/1107_chengfu_nodejs/index.html
網(wǎng)站題目:使用node.js進(jìn)行服務(wù)器端JavaScript編程
標(biāo)題鏈接:http://fisionsoft.com.cn/article/djgegoe.html


咨詢
建站咨詢
