
畢竟不是一個真正的教程,這裡主要還是以普及和介紹為主,是以這一部分就是
Node.js的其他部分介紹了,主要也就是事件觸發、作業系統以及流的知識。
1 事件觸發器
因為我們之前在浏覽器中使用
JavaScript,是以知道
JS通過事件處理了許多使用者的互動:滑鼠的單擊、鍵盤按鈕的按下、對滑鼠移動的反應等等。
在後端,
Node.js也提供了使用
events 子產品建構類似系統的選項。
具體上,此子產品提供了
EventEmitter類,用于處理事件。
使用以下代碼進行初始化:
const
該對象公開了
on和
emit方法:
- emit 用于觸發事件
- on 用于添加回調函數(會在事件被觸發時執行)
例如,建立
start事件,并提供一個示例,通過記錄到控制台進行互動:
eventEmitter
當運作以下代碼時:
eventEmitter
事件處理函數會被觸發,且獲得控制台日志。
可以通過将參數作為額外參數傳給
emit()來将參數傳給事件處理程式:
eventEmitter
多個參數:
eventEmitter
EventEmitter 對象還公開了其他幾個與事件進行互動的方法,例如:
- once() :添加單次監聽器
- removeListener() / off() :從事件中移除事件監聽器
- removeAllListeners() :移除事件的所有監聽器
下面詳細的介紹一下事件子產品:
2 事件子產品
events子產品為提供了
EventEmitter類,這是在
Node.js中處理事件的關鍵,如上文簡單的引入
events即可使用,這裡是另一個例子:
const
事件監聽器傳回及使用以下事件:
- 當監聽器被添加時傳回 newListener
- 當監聽器被移除時傳回 removeListener
以下是最常用的方法的詳細說明:
2.1 emitter.addListener()
emitter.on()的别名,這是為了和
DOM API保持一定的一緻(
[DOM].addEventListener())。
2.2 emitter.emit()
觸發事件, 按照事件被注冊的順序同步地調用每個事件監聽器:
door
2.3 emitter.eventNames()
傳回字元串(表示在目前
EventEmitter對象上注冊的事件)數組:
door
2.4 emitter.getMaxListeners()
擷取可以添加到
EventEmitter對象的監聽器的最大數量(預設為
10,但可以使用
setMaxListeners()進行增加或減少)。
door
2.5 emitter.listenerCount()
擷取作為參數傳入的事件監聽器的計數:
door
2.6 emitter.listeners()
擷取作為參數傳入的事件監聽器的數組:
door
2.7 emitter.off()
emitter.removeListener()的别名,新增于
Node.js 10;這是為了和
emitter.on()形成對應,簡化記憶。
2.8 emitter.on()
添加當事件被觸發時調用的回調函數,用法:
door
2.9 emitter.once()
添加當事件在注冊之後首次被觸發時調用的回調函數, 該回調隻會被調用一次,不會再被調用:
const
2.10 emitter.prependListener()
當使用
on或
addListener添加監聽器時,監聽器會被添加到監聽器隊列中的最後一個,并且最後一個被調用; 使用
prependListener則可以在其他監聽器之前添加并調用。
2.11 emitter.prependOnceListener()
當使用
once添加監聽器時,監聽器會被添加到監聽器隊列中的最後一個,并且最後一個被調用; 使用 prependOnceListener 則可以在其他監聽器之前添加并調用。
2.12 emitter.removeAllListeners()
移除
EventEmitter對象的所有監聽特定事件的監聽器:
door
2.13 emitter.removeListener()
移除特定的監聽器,可以通過将回調函數儲存到變量中(當添加時),以便以後可以引用它:
const
2.14 emitter.setMaxListeners()
設定可以添加到
EventEmitter對象的監聽器的最大數量(預設為
10,但可以增加或減少):
door
3 作業系統子產品
為了能夠真正的和作業系統互動,Node.js 同樣提供了作業系統子產品;該子產品提供了許多函數,可用于從底層的作業系統和程式運作所在的計算機上檢索資訊并與其進行互動,引用方法保持一緻:
const
有一些有用的屬性可以告訴我們一些與處理檔案有關的關鍵事項:
- os.EOL 可給出行定界符序列:在 Linux 和 macOS 上為 n ,在 Windows 上為 rn
- os.constants.signals 可告知所有與處理過程信号相關的常量,例如 SIGHUP 、 SIGKILL 等
- os.constants.errno 可設定用于錯誤報告的常量,例如 EADDRINUSE 、 EOVERFLOW 等
可以在 http://nodejs.cn/api/os.html#os_signal_constants 上閱讀所有的内容。
現在看一下
os提供的主要方法:
3.1 os.arch()
傳回辨別底層架構的字元串,例如
arm、
x64、
arm64:
3.2 os.cpus()
傳回關于系統上可用的
CPU的資訊。
例如:
3.3 os.endianness()
根據是使用大端序或小端序編譯
Node.js,傳回
BE或
LE:
3.4 os.freemem()
傳回代表系統中可用記憶體的位元組數:
3.5 os.homedir()
傳回到目前使用者的主目錄的路徑,例如:
3.6 os.hostname()
傳回主機名:
3.7 os.loadavg()
傳回作業系統對平均負載的計算,這僅在
Linux和
macOS上傳回有意義的值;
Windows因為對系統負載的計算方法不同,傳回值為
。
例如:
3.8 os.networkInterfaces()
傳回系統上可用的網絡接口的詳細資訊,例如(因為是我真實的電腦,是以我把
mac位址遮蔽了,見諒):
3.9 os.platform()
傳回為
Node.js編譯的平台:
- darwin
- freebsd
- linux
- openbsd
- win32
- ...等
3.10 os.release()
傳回辨別作業系統版本号的字元串:
3.11 os.tmpdir()
傳回指定的臨時檔案夾的路徑:
3.12 os.totalmem()
傳回表示系統中可用的總記憶體的位元組數:
3.13 os.type()
辨別作業系統:
- Linux
- macOS 上為 Darwin
- Windows 上為 Windows_NT (這是因為非 NT 架構的 Windows 系統現在占有率太低了,比如 Win 95、98、Me,是以預設如此顯示)
3.14 os.uptime()
傳回自上次重新啟動以來計算機持續運作的秒數:
3.15 os.userInfo()
目前登入使用者的資訊:
4 流
流是為
Node.js應用程式提供動力的基本概念之一,這是一種以高效的方式處理讀/寫檔案、網絡通信、或任何類型的端到端的資訊交換。
流不是
Node.js特有的概念,它是幾十年前在
Unix作業系統中引入的,程式可以通過管道運算符(
|)對流進行互相互動。
例如,在傳統的方式中,當告訴程式讀取檔案時,這會将檔案從頭到尾讀入記憶體,然後進行處理。
使用流,則可以逐個片段地讀取并處理(而無需全部儲存在記憶體中)。
Node.js的 stream 子產品 提供了建構所有流
API的基礎,所有的流都是
EventEmitter的執行個體(這樣講就容易明白了吧)。
4.1 為什麼使用流
相對于使用其他的資料處理方法,流基本上提供了兩個主要優點:
- 記憶體效率 :無需加載大量的資料到記憶體中即可進行處理
- 時間效率 :當獲得資料之後即可立即開始處理資料,這樣所需的時間更少,而不必等到整個資料有效負載可用才開始
4.2 流的示例
一個典型的例子是從磁盤讀取檔案。
使用
Node.js的
fs子產品,可以讀取檔案,并在與
HTTP伺服器建立新連接配接時通過
HTTP提供檔案:
const
readFile() 讀取檔案的全部内容,并在完成時調用回調函數;而回調中的
res.end(data)會傳回檔案的内容給
HTTP用戶端。
如果檔案很大,則該操作會花費較多的時間,這個時候我們可以使用流,如下例:
const
當要發送的資料塊已獲得時就立即開始将其流式傳輸到
HTTP用戶端,而不是等待直到檔案被完全讀取。
4.3 pipe()
上面的示例使用了
stream.pipe(res)這行代碼:在檔案流上調用
pipe()方法。
該代碼的作用是擷取來源流,并将其通過管道傳輸到目标流,在該示例中,檔案流通過管道傳輸到
HTTP響應。
pipe()方法的傳回值是目标流,它可以連結多個
pipe()調用,進而非常友善,如下所示:
src
此構造相對于:
src
要更容易了解和編寫。
4.4 流驅動的 Node.js API
由于它的優點,許多
Node.js核心子產品提供了原生的流處理功能,最值得注意的有:
- process.stdin 傳回連接配接到 stdin 的流
- process.stdout 傳回連接配接到 stdout 的流
- process.stderr 傳回連接配接到 stderr 的流
- fs.createReadStream() 建立檔案的可讀流
- fs.createWriteStream() 建立到檔案的可寫流
- net.connect() 啟動基于流的連接配接
- http.request() 傳回 http.ClientRequest 類的執行個體,該執行個體是可寫流
- zlib.createGzip() 使用 gzip (壓縮算法)将資料壓縮到流中
- zlib.createGunzip() 解壓縮 gzip 流
- zlib.createDeflate() 使用 deflate (壓縮算法)将資料壓縮到流中
- zlib.createInflate() 解壓縮 deflate 流
4.5 不同類型的流
流分為四類:
- Readable :可以通過管道讀取、但不能通過管道寫入的流(可以接收資料,但不能向其發送資料);當推送資料到可讀流中時,會對其進行緩沖,直到使用者開始讀取資料為止
- Writable :可以通過管道寫入、但不能通過管道讀取的流(可以發送資料,但不能從中接收資料)
- Duplex :可以通過管道寫入和讀取的流,基本上相對于是可讀流和可寫流的組合
- Transform :類似于雙工流、但其輸出是其輸入的轉換的轉換流
4.6 如何建立可讀流
從 stream 子產品擷取可讀流,對其進行初始化并實作
readable._read()方法。
首先建立流對象:
const
然後實作
_read:
readableStream
也可以使用
read選項實作
_read:
const
現在,流已初始化,可以向其發送資料了:
readableStream
4.7 如何建立可寫流
若要建立可寫流,需要繼承基本的
Writable對象,并實作其
_write()方法。
首先建立流對象:
const
然後實作
_write:
writableStream
現在,可以通過以下方式傳輸可讀流:
process
4.8 如何從可讀流中擷取資料
使用可寫流:
const
也可以使用
readable事件直接擷取可讀流資料:
readableStream
4.9 如何發送資料到可寫流
使用流的
write()方法:
writableStream
4.10 使用信号通知已結束寫入的可寫流
使用
end()方法:
const
眼看着基礎的
Node.js快要結束了,其實如果不算第三方的子產品,它自帶的東西的确就是這麼多,我們的重點還是要有子產品化開發的思路和方法論,或者大量的練習,畢竟熟能生巧麼。