1. 程式人生 > >nodejs學習筆記》入門級教程

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元件