nodejs學習筆記》入門級教程
簡單的說 Node.js 就是執行在服務端的 JavaScript。它的優勢是基於Google的V8引擎(執行速度非常快、效能非常好、社群活躍),更具誘惑力的是採用node+mongodb開發中小型網站速度更快(據說成本更低)。
目錄
1.1. Node簡介
1.2. 下載安裝
1.3. 模組機制
1.4. 非同步I/O
1.5. 非同步程式設計
1.6. 核心API
1.6.1. Event
events是Node.js最重要的模版,原因是Node.js本身架構就是事件式的,而它提供了唯一的介面。所以開成Node.js事件程式設計的基石。events模組不僅用於使用者程式碼與Node.js下層事件迴圈的互動。還幾乎被所有的模組依賴。
events模組只提供了一個物件(events.EventEmitter)。EventEmitter的核心就是事件發射與事件監聽器功能的封裝。EventEmitter的每個事件由一個事件或若干個引數組成,事件名是一個字串,通常表達一定的語義。對於每個事件,EventEmitter支援若干個事件監聽器。當事件發射時,註冊到這個事件的事件監聽器被依次呼叫,事件引數作為回撥函式引數傳遞。
常用API的方法:
(1)EventEmitter.on(event,listener)為指定事件註冊一個監聽器,接受一個字串event和一個回撥函式listener
(2)EventEmitter.emit(event,[arg1],[arg2]....)發射event事件,傳遞若干可選引數到事件監聽器的引數表
(3)EventEmitter.once(event,listener) 為指定事件註冊一個單次監聽器,即監聽器最多隻會觸發一次,觸發後立刻解除該監聽器。
(4)EventEmitter.removeListener(event,listener)移除指定事件的某個監聽器,listener必須是該事件已經註冊過的監聽器。
(5)EventEmitter.removeAllListeners([event])移除所有事件的所有監聽器,如果指定event,則移除指定事件的所有監聽器。
事件
1.普通事件的使用
//宣告事件物件` var EventEmitter=require('events').EventEmitter; var event=newEventEmitter(); //註冊事件 event.on('some_event', function() { console.log('這是一個自定義的事件'); }); //觸發事件 setTimeout(function() { event.emit('some_event'); }, 1000); console.log("event...")
2.Node.js的事件迴圈機制
(1)Node.js在什麼時候進入事件迴圈呢?
Node.js程式是由事件迴圈開始,到事件迴圈結束,所有的邏輯都是事件的回撥函式。
(2)如何使用自定義事件呢?
事件的回撥函式在執行的過程中,可能會發出IO請求或直接發射(emit)事件,執行完畢後再返回事件迴圈。
回撥函式
非同步式讀取檔案:
var fs=require('fs');
fs.readFile('file.txt', 'utf-8', function(err, data) {
if (err) {
console.log(err)
} else {
console.log(data)
}
});
console.log('end.')
同步式讀取檔案:
var fs=require('fs');
var data=fs.readFileSync('file.txt', 'utf-8');
console.log(data)
console.log('end.')
分析:
呼叫時所做的工作知識將非同步式IO請求傳送給了作業系統,然後立即返回並執行後面的語句,執行完以後進入事件迴圈監聽事件,當fs接受到IO請求完成的事件時。事件迴圈會主動呼叫回撥函式完成後續工作。同步則是阻塞等待完成後,繼續執行。
1.6.2. HTTP
Node.js 的核心功能之一就是作為Web 伺服器,這是這個系統的一個重要部分,所以當 Ryan Dahl 發起此專案時,他為 V8 重寫了 HTTP 模組,使其能夠非阻塞執行。雖然最開始的 HTTP 實現已經蛻變了許多,API 和內部實現不斷升級,但核心操作還是保持不變的。Node 實現的 HTTP 模組是非阻塞的,且速度很快,其中許多程式碼已經從 C 遷移到了 JavaScript。HTTP 使用的模式在 Node 裡很常見。父工廠類提供了很容易建立新伺服器的方法。http.createServer() 方法為我們提供了構建新的 HTTPServer 類的例項。我們可以為新的類定義 Node 接受到 HTTP 請求時的操作。HTTP 模組及其他 Node 模組還有一些共性的地方,比如 Server 類能夠觸發的事件,還有傳給回撥函式的資料
結構。瞭解這 3 種類型有助於你更好地使用 HTTP 模組。
Http伺服器
var http=require('http');
var server=http.createServer();
var handleReq=function(req, res) {
res.writeHead(200, {});
res.end('hello world');
};
server.on('request', handleReq);
server.listen(8125);
console.warn("localhost is running")
Http客戶端
如果你想向遠端伺服器發起 HTTP 連線,Node 也是很好的選擇。Node 在許多情下都很適合使用,如使用 Web service,連線到文件資料庫,或是抓取網頁。你可使用同樣的 http 模組來發起 HTTP 請求,但應該使用 http.ClientRequest 類該類有兩個工廠方法:一個通用的方法和一個便捷的方法。
var http=require('http');
var opts= {
host:'www.baidu.com',
port:80,
path:'/',
method:'GET'
};
var req=http.request(opts, function(res) {
console.log(res);
res.on('data', function(data) {
console.log(data);
});
});
req.end()
URL
var URL=require('url');
var myUrl="http://www.nodejs.org/some/url/?with=query";
console.log(myUrl)
var parsedUrl=URL.parse(myUrl);
console.log(parsedUrl)
querystring
querystring 模組是用來處理 query 字串的簡單輔助模組。上一小節已經討論過,query 字串是在 URL 尾部編碼過的引數。但是如果只是把它當做 JavaScript字串來使用時,處理這些引數未免很煩瑣。querystring 模組提供了從 query字串中輕鬆提取物件的方法。它的主要功能有 parse 和 decode,還包括一些內部輔助函式,如 escape、unescape、unescapeBuffer、encode 和 stringify。如果你有一個 query 字串,你可以使用 parse 來把它變成一個物件。
var qs=require('querystring');
console.log(qs.parse('a=1&b=2&c=d'))
1.6.3. I/O
資料流
可讀的資料流API是一組方法和事件,提供了資料來源在傳送時訪問資料塊的功能。
基本上,可讀資料流是與觸發 data 事件相關的
var fs=require('fs');
var filehandle=fs.readFile('file.txt', function(err, data) {
console.log(data)
});
有時候我們需要等待完整的資料都可用後再進行操作,在這種情況下就會用到資料池模式(spooling pattern)。我們知道重點是不要讓 Node 的事件迴圈阻塞,所以即使不想在接收到所有資料之前進行下一步處理,也不希望堵塞事件迴圈。在這種情況下,我們使用資料流來讀取資料,但只有在接收到足夠的內容後才使用這些資料。
var spool="";
stream.on('data', function(data) {
spool+=data;
});
stream.on('end', function() {
console.log(spool);
});
檔案系統
檔案系統模組顯然非常有用,因為你需要它來訪問磁碟上的檔案。它幾乎模仿了文
件 I/O 的 POSIX 風格,示例:讀取並刪除檔案——但這是錯誤的(它是非同步的)
var fs=require('fs');
fs.readFile('warandpeace.txt', function(e, data) {
console.log('War and Peace: '+data);
});
fs.unlink('warandpeace.txt');
//正確的是:
var fs=require('fs');
fs.readFile('warandpeace.txt', function(e, data) {
console.log('War and Peace: '+data);
fs.unlink('warandpeace.txt');
});
Buffer
瀏覽器需要JavaScript 來進行許多操作,但並不包括處理二進位制資料。雖然說JavaScript 支援位元組位操作,但它並沒有二進位制資料的原生表現形式。Node 帶來了 Buffer 類,為你操作二進位制資料彌補了短板。Buffer 是 V8 引擎上的擴充套件,這意味著它有其固有的一些限制。Buffer 實際上是對記憶體的直接分配,這意味著這多少受制於你在低階計算機語言方面的經驗。
支援的編碼型別:ASCII、UTF-8(預設)、UTF-16、Base64、Binary、Hex
//不支援的編碼格式
console.log(Buffer.isEncoding('gbk'))
//字串轉Buffer
var bf=newBuffer('foobarbaz')
console.log(bf)
//Buffer轉字串
var foo=bf.toString()
console.log(foo)
console.log
這個簡單的 console.log 命令借用了 Firefox 中 Firebug 偵錯程式的概念,讓你可以
輕鬆把輸出列印到標準輸出(stdout)
1.7. 全域性物件&全域性變數
在JavaScript中,通常window是全域性物件,而Node.js的全域性物件是global,所有全域性變數都是global物件的屬性,如:console、process等。
全域性變數
global最根本的作用是作為全域性變數的宿主,滿足以下條件成為全域性變數:
1.在最外層定義的變數
2.全域性物件的屬性
3.隱式定義的變數(未定義直接賦值的變數)
在Node.js中不可能在最外層定義變數,因為所有使用者程式碼都是屬於當前模組的,而模組本身不是最外層上下文。
process
它用於描述當前Node.js程序狀態的物件。提供了一個與作業系統的簡單介面,通常寫本地命令列程式的時候,會用到它。
1.process.argv是命令列引數陣列,第一個元素是node,第二個元素是指令碼檔名,第三個元素開始每個元素是一個執行引數。
2.process.stdout是標準輸出流,通常我們使用的console.log() 其底層是用process.stdout.write();實現。
3.prcess.stdin是標準輸入流,初始時它是被暫停的。要想從標準輸入流讀取資料,必須恢復流,並手動編寫流的事件相應函式。
4.process.nextTick(callback)的功能是為事件迴圈設定一項任務。Node.js會在下次事件迴圈調響應時呼叫callback,ode.js適合IO密集型的應用,而不是計算密集型的應用。process.nextTick()提供了一個這樣工具,可以把複雜的工作拆散,編較小的事件程一個。
function doSomething(args, callback) {
somethingComplited(args);
callback();
}
doSomething('12345', function onEnd() {
compute();
})
//如果假設compute()和somethingComplited()是兩個較為耗時的函式。 以上的程式在呼叫doSomething時會先執行somethingComplited(args), 然後立即呼叫回撥函式,在onEnd()中又會執行compute(),改寫為:
function doSomething(args, callback) {
somethingComplited(args);
process.nextTick(callback);
}
使用process.nextTick() 後,改寫後的程式會把上面耗時的操作拆分為兩個事件,減少每個事件的執行時間,提高事件相應速度。
5.process還提供了process.pid、process.execPath、process.memoryUsage()等
1.8. util模組
util.log(string)
將 string 引數的內容加上當前時間戳,輸出到 stdout 標準輸出
require('util').log('Timestmaped message.');
輸出: 14 Jun 16:02:06 - Timestmaped message.
util.inspect(object, showHidden=false, depth=2)
以字串形式返回 object 物件的結構資訊,這對程式除錯非常有幫助
var util=require('util');
console.log(util.inspect(util, true, null));
util.inherits(constructor, superConstructor)
實現物件間原型繼承的函式
var util=require("util");
var events=require("events");
function MyStream() {
events.EventEmitter.call(this);
}
util.inherits(MyStream, events.EventEmitter);
MyStream.prototype.write=function(data) {
this.emit("data", data);
}
var stream=newMyStream();
console.log(streaminstanceofevents.EventEmitter); // true
console.log(MyStream.super_===events.EventEmitter); // true
stream.on("data", function(data) {
console.log('Received data: "'+data+'"');
})
stream.write("It works!"); // Received data: "It works!"
1.9. assert斷言
斷言(Assert)模組用於為應用編寫單元測試,可以通過 require('assert')對該模組進行呼叫。
var assert=require('assert');
assert.equal(1, true, 'Truthy');
assert.notEqual(1, true, 'Truthy');
1.10. npm
1.10.1. package.json
package.json位於模組的目錄下,用於定義包的屬性。我們可以看下 express 包位於 node_modules/express/package.json檔案
Key |
描述 |
name |
- 包名。 |
version |
- 包的版本號。 |
description |
- 包的描述。 |
homepage |
- 包的官網 url 。 |
author |
- 包的作者姓名。 |
contributors |
- 包的其他貢獻者姓名。 |
dependencies |
- 依賴包列表。如果依賴包沒有安裝,npm 會自動將依賴包安裝在 node_module 目錄下。 |
repository |
- 包程式碼存放的地方的型別,可以是 git 或 svn,git 可在 Github 上。 |
main |
- main 欄位是一個模組ID,它是一個指向你程式的主要專案。就是說,如果你包的名字叫 express,然後使用者安裝它,然後require("express")。 |
keywords |
- 關鍵字 |
1.10.2. 命令
命令列 |
描述 |
npm –v |
顯示版本 |
npm config list |
檢視配置 npm config set prefix "D:\Program\nodejs\node_global" npm config set cache "D:\Program\nodejs\node_cache" |
npm install express |
安裝或升級express(web框架模組) 程式碼中:var express = require('express'); -g全域性安裝 --save-dev 安裝本地並儲存到package.json中 npm install [email protected] 指定版本 |
npm ls |
檢視已安裝的模組 -g 檢視全域性已安裝的模組 |
npm uninstall express |
解除安裝模組 |
npm update express |
更新模組 |
npm search express |
搜尋模組 |
npm init |
建立模組 |
npm publish |
釋出模組 |
npm cache clear |
可以清空NPM本地快取 |
npm unpublish <package>@<version> |
撤銷釋出自己釋出過的某個版本程式碼 |
1.10.3. 遠端映象
大家都知道國內直接使npm的官方映象是非常慢的,
l 淘寶npm映象
l cnpmjs映象
配置npm的registry地址:
l 臨時使用
npm --registry https://registry.npm.taobao.org installexpress
l 持久使用
npm config set registryhttps://registry.npm.taobao.org
npm config get registry // 配置後可通過下面方式來驗證是否成功
npm info express // 或
當然你可以使用淘寶定製的cnpm (gzip 壓縮支援) 命令列工具代替預設的 npm。
npm install -gcnpm --registry=https://registry.npm.taobao.org
cnpm install[name]
1.11. Cluster模組
Node.js預設單程序執行,對於32位系統最高可以使用512MB記憶體,對於64位最高可以使用1GB記憶體。對於多核CPU的計算機來說,這樣做效率很低,因為只有一個核在執行,其他核都在閒置。cluster模組就是為了解決這個問題而提出的。
cluster模組允許設立一個主程序和若干個worker程序,由主程序監控和協調worker程序的執行。worker之間採用程序間通訊交換訊息,cluster模組內建一個負載均衡器,採用Round-robin演算法協調各個worker程序之間的負載。執行時,所有新建立的連結都由主程序完成,然後主程序再把TCP連線分配給指定的worker程序。
1.12. PM2模組
PM2模組是cluster模組的一個包裝層。它的作用是儘量將cluster模組抽象掉,讓使用者像使用單程序一樣,部署多程序Node應用。
好了,掌握了這一篇我們可以瞭解下一篇《常用node元件》