• Node.js Video Tutorials

Node.js 快速指南



Node.js - 簡介

什麼是 Node.js?

Node.js 是一個基於 Google Chrome 的 JavaScript 引擎 (V8 引擎) 的伺服器端平臺。Node.js 由 Ryan Dahl 於 2009 年開發,最新版本為 v0.10.36。根據其官方文件提供的定義如下:

Node.js 是一個基於Chrome 的 JavaScript 執行時環境構建的平臺,用於輕鬆構建快速且可擴充套件的網路應用程式。Node.js 使用事件驅動、非阻塞 I/O 模型,使其輕量且高效,非常適合執行在分散式裝置上的資料密集型即時應用程式。

Node.js 是一個開源的、跨平臺的執行時環境,用於開發伺服器端和網路應用程式。Node.js 應用程式是用 JavaScript 編寫的,可以在 OS X、Microsoft Windows 和 Linux 上的 Node.js 執行時環境中執行。

Node.js 還提供豐富的各種 JavaScript 模組庫,在很大程度上簡化了使用 Node.js 開發 Web 應用程式的過程。

Node.js = Runtime Environment + JavaScript Library

Node.js 的特性

以下是使 Node.js 成為軟體架構師首選的一些重要特性。

  • 非同步和事件驅動 - Node.js 庫的所有 API 都是非同步的,即非阻塞的。它本質上意味著基於 Node.js 的伺服器永遠不會等待 API 返回資料。伺服器在呼叫 API 後會移動到下一個 API,而 Node.js 的事件通知機制幫助伺服器從之前的 API 呼叫中獲取響應。

  • 非常快 - Node.js 庫構建於 Google Chrome 的 V8 JavaScript 引擎之上,程式碼執行速度非常快。

  • 單執行緒但高度可擴充套件 - Node.js 使用單執行緒模型和事件迴圈。事件機制幫助伺服器以非阻塞方式響應,並使伺服器具有高度可擴充套件性,這與建立有限執行緒來處理請求的傳統伺服器相反。Node.js 使用單執行緒程式,並且同一個程式可以比 Apache HTTP Server 等傳統伺服器提供服務給更多數量的請求。

  • 無緩衝 - Node.js 應用程式從不緩衝任何資料。這些應用程式只是分塊輸出資料。

  • 許可證 - Node.js 在MIT 許可證下發布。

誰在使用 Node.js?

以下是 github wiki 上的連結,其中包含使用 Node.js 的專案、應用程式和公司的詳盡列表。此列表包括 eBay、通用電氣、GoDaddy、微軟、PayPal、Uber、Wikipins、雅虎和 Yammer 等。

概念

下圖描述了 Node.js 的一些重要部分,我們將在後續章節中詳細討論。

Node.js Concepts

在哪裡使用 Node.js?

以下是 Node.js 證明自己是完美技術合作夥伴的領域。

  • I/O 密集型應用程式
  • 資料流應用程式
  • 資料密集型即時應用程式 (DIRT)
  • 基於 JSON API 的應用程式
  • 單頁應用程式

在哪裡不使用 Node.js?

對於 CPU 密集型應用程式,不建議使用 Node.js。

Node.js - 環境搭建

線上試用選項

您實際上不需要設定自己的環境來開始學習 Node.js。原因很簡單,我們已經在網上設定了 Node.js 環境,這樣您就可以線上執行所有可用的示例並透過實踐學習。隨意修改任何示例並使用不同的選項檢查結果。

使用以下示例,使用以下示例程式碼框(在我們的網站上)右上角的即時演示選項:

/* Hello World! program in Node.js */
console.log("Hello World!");

對於本教程中給出的大多數示例,您都會找到一個“試用”選項,因此只需使用它即可享受學習的樂趣。

本地環境搭建

如果您仍然願意為 Node.js 設定環境,則需要在您的計算機上安裝以下兩個軟體:(a)文字編輯器和(b)Node.js 二進位制安裝程式。

文字編輯器

這將用於鍵入您的程式。一些編輯器的示例包括 Windows 記事本、OS Edit 命令、Brief、Epsilon、EMACS 和 vim 或 vi。

文字編輯器的名稱和版本在不同的作業系統上可能會有所不同。例如,Windows 上將使用記事本,而 vim 或 vi 也可以在 Windows 以及 Linux 或 UNIX 上使用。

您使用編輯器建立的檔案稱為原始檔,其中包含程式原始碼。Node.js 程式的原始檔通常以副檔名“.js”命名。

在開始程式設計之前,請確保您已安裝一個文字編輯器,並且您有足夠的經驗來編寫計算機程式,將其儲存在檔案中,最後執行它。

Node.js 執行時

在原始檔中編寫的原始碼只是 JavaScript。Node.js 直譯器將用於解釋和執行您的 JavaScript 程式碼。

Node.js 發行版作為 SunOS、Linux、Mac OS X 和 Windows 作業系統的二進位制安裝程式提供,具有 32 位 (386) 和 64 位 (amd64) x86 處理器架構。

以下部分指導您如何在各種作業系統上安裝 Node.js 二進位制發行版。

下載 Node.js 存檔

Node.js 下載下載最新版本的 Node.js 可安裝存檔檔案。在撰寫本教程時,不同作業系統上可用的版本如下。

作業系統 存檔名稱
Windows node-v6.3.1-x64.msi
Linux node-v6.3.1-linux-x86.tar.gz
Mac node-v6.3.1-darwin-x86.tar.gz
SunOS node-v6.3.1-sunos-x86.tar.gz

在 UNIX/Linux/Mac OS X 和 SunOS 上安裝

根據您的作業系統架構,下載並將存檔 node-v6.3.1-**osname**.tar.gz 解壓縮到 /tmp,然後最終將解壓縮的檔案移動到 /usr/local/nodejs 目錄。例如

$ cd /tmp
$ wget http://nodejs.org/dist/v6.3.1/node-v6.3.1-linux-x64.tar.gz
$ tar xvfz node-v6.3.1-linux-x64.tar.gz
$ mkdir -p /usr/local/nodejs
$ mv node-v6.3.1-linux-x64/* /usr/local/nodejs

將 /usr/local/nodejs/bin 新增到 PATH 環境變數。

作業系統 輸出
Linux export PATH=$PATH:/usr/local/nodejs/bin
Mac export PATH=$PATH:/usr/local/nodejs/bin
FreeBSD export PATH=$PATH:/usr/local/nodejs/bin

在 Windows 上安裝

使用 MSI 檔案並按照提示安裝 Node.js。預設情況下,安裝程式使用 C:\Program Files\nodejs 中的 Node.js 發行版。安裝程式應在 Windows 的 PATH 環境變數中設定 C:\Program Files\nodejs\bin 目錄。重新啟動任何開啟的命令提示符以使更改生效。

驗證安裝:執行檔案

在您的機器(Windows 或 Linux)上建立一個名為main.js的 js 檔案,其中包含以下程式碼。

/* Hello, World! program in node.js */
console.log("Hello, World!")

現在使用 Node.js 直譯器執行 main.js 檔案以檢視結果:

$ node main.js

如果您的安裝一切正常,則應產生以下結果:

Hello, World!

Node.js - 第一個應用程式

在使用 Node.js 建立實際的“Hello, World!”應用程式之前,讓我們看看 Node.js 應用程式的元件。Node.js 應用程式包含以下三個重要元件:

  • 匯入所需的模組 - 我們使用require指令載入 Node.js 模組。

  • 建立伺服器 - 一個伺服器,它將監聽客戶端的請求,類似於 Apache HTTP 伺服器。

  • 讀取請求並返回響應 - 在前面步驟中建立的伺服器將讀取客戶端(可以是瀏覽器或控制檯)發出的 HTTP 請求並返回響應。

建立 Node.js 應用程式

步驟 1 - 匯入所需的模組

我們使用require指令載入 http 模組並將返回的 HTTP 例項儲存到 http 變數中,如下所示:

var http = require("http");

步驟 2 - 建立伺服器

我們使用建立的 http 例項並呼叫http.createServer()方法來建立一個伺服器例項,然後我們使用與伺服器例項關聯的listen方法將其繫結到 8081 埠。向其傳遞一個帶有 request 和 response 引數的函式。編寫始終返回“Hello World”的示例實現。

http.createServer(function (request, response) {
   // Send the HTTP header 
   // HTTP Status: 200 : OK
   // Content Type: text/plain
   response.writeHead(200, {'Content-Type': 'text/plain'});
   
   // Send the response body as "Hello World"
   response.end('Hello World\n');
}).listen(8081);

// Console will print the message
console.log('Server running at http://127.0.0.1:8081/');

以上程式碼足以建立一個 HTTP 伺服器,該伺服器監聽,即等待本地機器 8081 埠上的請求。

步驟 3 - 測試請求和響應

讓我們將步驟 1 和 2 組合到一個名為main.js的檔案中,並啟動我們的 HTTP 伺服器,如下所示:

var http = require("http");

http.createServer(function (request, response) {
   // Send the HTTP header 
   // HTTP Status: 200 : OK
   // Content Type: text/plain
   response.writeHead(200, {'Content-Type': 'text/plain'});
   
   // Send the response body as "Hello World"
   response.end('Hello World\n');
}).listen(8081);

// Console will print the message
console.log('Server running at http://127.0.0.1:8081/');

現在執行 main.js 以啟動伺服器,如下所示:

$ node main.js

驗證輸出。伺服器已啟動。

Server running at http://127.0.0.1:8081/

向 Node.js 伺服器發出請求

在任何瀏覽器中開啟 http://127.0.0.1:8081/ 並觀察以下結果。

Node.js Sample

恭喜,您已經啟動並運行了第一個 HTTP 伺服器,它正在響應 8081 埠上的所有 HTTP 請求。

Node.js - REPL 終端

REPL 代表 Read Eval Print Loop(讀取-求值-列印-迴圈),它代表一個類似於 Windows 控制檯或 Unix/Linux shell 的計算機環境,其中輸入命令,系統以互動模式響應輸出。Node.js 或Node捆綁了 REPL 環境。它執行以下任務:

  • 讀取 - 讀取使用者的輸入,將輸入解析為 JavaScript 資料結構,並將其儲存在記憶體中。

  • 求值 - 獲取並計算資料結構。

  • 列印 - 列印結果。

  • 迴圈 - 迴圈執行上述命令,直到使用者按下ctrl-c兩次。

Node 的 REPL 功能在試驗 Node.js 程式碼和除錯 JavaScript 程式碼方面非常有用。

線上REPL終端

為了簡化您的學習,我們設定了一個易於使用的線上Node.js REPL環境,您可以在其中練習Node.js語法 − 啟動Node.js REPL終端

啟動REPL

只需在shell/控制檯中執行node(無需任何引數)即可啟動REPL,如下所示。

$ node

您將看到REPL命令提示符>,您可以在其中鍵入任何Node.js命令 −

$ node
>

簡單表示式

讓我們在Node.js REPL命令提示符下嘗試一個簡單的數學運算 −

$ node
> 1 + 3
4
> 1 + ( 2 * 3 ) - 4
3
>

使用變數

您可以使用變數來儲存值並在稍後列印,就像任何傳統的指令碼一樣。如果未使用var關鍵字,則值將儲存在變數中並打印出來。而如果使用var關鍵字,則值將被儲存但不會被列印。您可以使用console.log()列印變數。

$ node
> x = 10
10
> var y = 10
undefined
> x + y
20
> console.log("Hello World")
Hello World
undefined

多行表示式

Node REPL支援與JavaScript類似的多行表示式。讓我們檢查一下以下do-while迴圈的執行情況 −

$ node
> var x = 0
undefined
> do {
   ... x++;
   ... console.log("x: " + x);
   ... } 
while ( x < 5 );
x: 1
x: 2
x: 3
x: 4
x: 5
undefined
>

當您在開括號後按Enter鍵時,...會自動出現。Node會自動檢查表示式的連續性。

下劃線變數

您可以使用下劃線(_)來獲取最後一個結果 −

$ node
> var x = 10
undefined
> var y = 20
undefined
> x + y
30
> var sum = _
undefined
> console.log(sum)
30
undefined
>

REPL命令

  • ctrl + c − 終止當前命令。

  • 兩次ctrl + c − 終止Node REPL。

  • ctrl + d − 終止Node REPL。

  • 向上/向下鍵 − 檢視命令歷史記錄並修改之前的命令。

  • Tab鍵 − 當前命令列表。

  • .help − 所有命令的列表。

  • .break − 從多行表示式退出。

  • .clear − 從多行表示式退出。

  • .save 檔名 − 將當前Node REPL會話儲存到檔案。

  • .load 檔名 − 在當前Node REPL會話中載入檔案內容。

停止REPL

如上所述,您需要使用兩次ctrl-c才能退出Node.js REPL。

$ node
>
(^C again to quit)
>

Node.js - NPM

Node包管理器(NPM)提供兩個主要功能 −

  • Node.js包/模組的線上儲存庫,可在search.nodejs.org上搜索

  • 命令列實用程式,用於安裝Node.js包,進行版本管理和Node.js包的依賴項管理。

v0.6.3版本之後,NPM與Node.js安裝程式捆綁在一起。要驗證這一點,請開啟控制檯並鍵入以下命令並檢視結果 −

$ npm --version
2.7.1

如果您執行的是舊版本的NPM,則將其更新到最新版本非常容易。只需從根目錄使用以下命令 −

$ sudo npm install npm -g
/usr/bin/npm -> /usr/lib/node_modules/npm/bin/npm-cli.js
npm@2.7.1 /usr/lib/node_modules/npm

使用NPM安裝模組

安裝任何Node.js模組都採用簡單的語法 −

$ npm install <Module Name>

例如,以下是安裝一個名為express的著名Node.js Web框架模組的命令 −

$ npm install express

現在您可以像下面這樣在您的js檔案中使用此模組 −

var express = require('express');

全域性安裝與本地安裝

預設情況下,NPM以本地模式安裝任何依賴項。這裡的本地模式是指在Node應用程式所在的資料夾中的node_modules目錄中安裝包。本地部署的包可以透過require()方法訪問。例如,當我們安裝express模組時,它在安裝express模組的當前目錄中建立了node_modules目錄。

$ ls -l
total 0
drwxr-xr-x 3 root root 20 Mar 17 02:23 node_modules

或者,您可以使用npm ls命令列出所有已本地安裝的模組。

全域性安裝的包/依賴項儲存在系統目錄中。此類依賴項可以在任何Node.js的CLI(命令列介面)函式中使用,但不能直接在Node應用程式中使用require()匯入。現在讓我們嘗試使用全域性安裝來安裝express模組。

$ npm install express -g

這將產生類似的結果,但模組將被全域性安裝。這裡,第一行顯示模組版本及其安裝位置。

express@4.12.2 /usr/lib/node_modules/express
├── merge-descriptors@1.0.0
├── utils-merge@1.0.0
├── cookie-signature@1.0.6
├── methods@1.1.1
├── fresh@0.2.4
├── cookie@0.1.2
├── escape-html@1.0.1
├── range-parser@1.0.2
├── content-type@1.0.1
├── finalhandler@0.3.3
├── vary@1.0.0
├── parseurl@1.3.0
├── content-disposition@0.5.0
├── path-to-regexp@0.1.3
├── depd@1.0.0
├── qs@2.3.3
├── on-finished@2.2.0 (ee-first@1.1.0)
├── etag@1.5.1 (crc@3.2.1)
├── debug@2.1.3 (ms@0.7.0)
├── proxy-addr@1.0.7 (forwarded@0.1.0, ipaddr.js@0.1.9)
├── send@0.12.1 (destroy@1.0.3, ms@0.7.0, mime@1.3.4)
├── serve-static@1.9.2 (send@0.12.2)
├── accepts@1.2.5 (negotiator@0.5.1, mime-types@2.0.10)
└── type-is@1.6.1 (media-typer@0.3.0, mime-types@2.0.10)

您可以使用以下命令檢查所有全域性安裝的模組 −

$ npm ls -g

使用package.json

package.json位於任何Node應用程式/模組的根目錄中,用於定義包的屬性。讓我們開啟位於node_modules/express/中的express包的package.json

{
   "name": "express",
      "description": "Fast, unopinionated, minimalist web framework",
      "version": "4.11.2",
      "author": {
      
         "name": "TJ Holowaychuk",
         "email": "tj@vision-media.ca"
      },
   
   "contributors": [{
      "name": "Aaron Heckmann",
      "email": "aaron.heckmann+github@gmail.com"
   }, 
   
   {
      "name": "Ciaran Jessup",
      "email": "ciaranj@gmail.com"
   },
   
   {
      "name": "Douglas Christopher Wilson",
      "email": "doug@somethingdoug.com"
   },
   
   {
      "name": "Guillermo Rauch",
      "email": "rauchg@gmail.com"
   },
   
   {
      "name": "Jonathan Ong",
      "email": "me@jongleberry.com"
   },
   
   {
      "name": "Roman Shtylman",
      "email": "shtylman+expressjs@gmail.com"
   },
   
   {
      "name": "Young Jae Sim",
      "email": "hanul@hanul.me"
   } ],
   
   "license": "MIT", "repository": {
      "type": "git",
      "url": "https://github.com/strongloop/express"
   },
   
   "homepage": "https://expressjs.com/", "keywords": [
      "express",
      "framework",
      "sinatra",
      "web",
      "rest",
      "restful",
      "router",
      "app",
      "api"
   ],
   
   "dependencies": {
      "accepts": "~1.2.3",
      "content-disposition": "0.5.0",
      "cookie-signature": "1.0.5",
      "debug": "~2.1.1",
      "depd": "~1.0.0",
      "escape-html": "1.0.1",
      "etag": "~1.5.1",
      "finalhandler": "0.3.3",
      "fresh": "0.2.4",
      "media-typer": "0.3.0",
      "methods": "~1.1.1",
      "on-finished": "~2.2.0",
      "parseurl": "~1.3.0",
      "path-to-regexp": "0.1.3",
      "proxy-addr": "~1.0.6",
      "qs": "2.3.3",
      "range-parser": "~1.0.2",
      "send": "0.11.1",
      "serve-static": "~1.8.1",
      "type-is": "~1.5.6",
      "vary": "~1.0.0",
      "cookie": "0.1.2",
      "merge-descriptors": "0.0.2",
      "utils-merge": "1.0.0"
   },
   
   "devDependencies": {
      "after": "0.8.1",
      "ejs": "2.1.4",
      "istanbul": "0.3.5",
      "marked": "0.3.3",
      "mocha": "~2.1.0",
      "should": "~4.6.2",
      "supertest": "~0.15.0",
      "hjs": "~0.0.6",
      "body-parser": "~1.11.0",
      "connect-redis": "~2.2.0",
      "cookie-parser": "~1.3.3",
      "express-session": "~1.10.2",
      "jade": "~1.9.1",
      "method-override": "~2.3.1",
      "morgan": "~1.5.1",
      "multiparty": "~4.1.1",
      "vhost": "~3.0.0"
   },
   
   "engines": {
      "node": ">= 0.10.0"
   },
   
   "files": [
      "LICENSE",
      "History.md",
      "Readme.md",
      "index.js",
      "lib/"
   ],
   
   "scripts": {
      "test": "mocha --require test/support/env 
         --reporter spec --bail --check-leaks test/ test/acceptance/",
      "test-cov": "istanbul cover node_modules/mocha/bin/_mocha 
         -- --require test/support/env --reporter dot --check-leaks test/ test/acceptance/",
      "test-tap": "mocha --require test/support/env 
         --reporter tap --check-leaks test/ test/acceptance/",
      "test-travis": "istanbul cover node_modules/mocha/bin/_mocha 
         --report lcovonly -- --require test/support/env 
         --reporter spec --check-leaks test/ test/acceptance/"
   },
   
   "gitHead": "63ab25579bda70b4927a179b580a9c580b6c7ada",
   "bugs": {
      "url": "https://github.com/strongloop/express/issues"
   },
   
   "_id": "express@4.11.2",
   "_shasum": "8df3d5a9ac848585f00a0777601823faecd3b148",
   "_from": "express@*",
   "_npmVersion": "1.4.28",
   "_npmUser": {
      "name": "dougwilson",
      "email": "doug@somethingdoug.com"
   },
   
   "maintainers": [{
      "name": "tjholowaychuk",
      "email": "tj@vision-media.ca"
   },
   
   {
      "name": "jongleberry",
      "email": "jonathanrichardong@gmail.com"
   },
   
   {
      "name": "shtylman",
      "email": "shtylman@gmail.com"
   },
   
   {
      "name": "dougwilson",
      "email": "doug@somethingdoug.com"
   },
   
   {
      "name": "aredridel",
      "email": "aredridel@nbtsc.org"
   },
   
   {
      "name": "strongloop",
      "email": "callback@strongloop.com"
   },
   
   {
      "name": "rfeng",
      "email": "enjoyjava@gmail.com"
   }],
   
   "dist": {
      "shasum": "8df3d5a9ac848585f00a0777601823faecd3b148",
      "tarball": "https://registry.npmjs.org/express/-/express-4.11.2.tgz"
   },
   
   "directories": {},
      "_resolved": "https://registry.npmjs.org/express/-/express-4.11.2.tgz",
      "readme": "ERROR: No README data found!"
}

Package.json的屬性

  • name − 包的名稱

  • version − 包的版本

  • description − 包的描述

  • homepage − 包的主頁

  • author − 包的作者

  • contributors − 包的貢獻者姓名

  • dependencies − 依賴項列表。NPM會自動安裝此處在包的node_module資料夾中提到的所有依賴項。

  • repository − 包的儲存庫型別和URL

  • main − 包的入口點

  • keywords − 關鍵詞

解除安裝模組

使用以下命令解除安裝Node.js模組。

$ npm uninstall express

NPM解除安裝包後,您可以檢視/node_modules/目錄的內容或鍵入以下命令來驗證它 −

$ npm ls

更新模組

更新package.json並更改要更新的依賴項的版本,然後執行以下命令。

$ npm update express

搜尋模組

使用NPM搜尋包名。

$ npm search express

建立模組

建立模組需要生成package.json。讓我們使用NPM生成package.json,這將生成package.json的基本框架。

$ npm init
This utility will walk you through creating a package.json file.
It only covers the most common items, and tries to guess sane defaults.

See 'npm help json' for definitive documentation on these fields
and exactly what they do.

Use 'npm install <pkg> --save' afterwards to install a package and
save it as a dependency in the package.json file.

Press ^C at any time to quit.
name: (webmaster)

您需要提供有關模組的所有必要資訊。您可以參考上面提到的package.json檔案來了解各種所需資訊的含義。生成package.json後,使用以下命令使用有效的電子郵件地址在NPM儲存庫網站上註冊自己。

$ npm adduser
Username: mcmohd
Password:
Email: (this IS public) mcmohd@gmail.com

現在是釋出您的模組的時候了 −

$ npm publish

如果您的模組一切正常,它將釋出到儲存庫中,並且可以使用NPM像任何其他Node.js模組一樣安裝。

Node.js - 回撥函式概念

什麼是回撥?

回撥是函式的非同步等效項。在完成給定任務後呼叫回撥函式。Node大量使用回撥。Node的所有API都是以支援回撥的方式編寫的。

例如,讀取檔案的函式可能會開始讀取檔案並立即將控制權返回給執行環境,以便可以執行下一條指令。檔案I/O完成後,它將呼叫回撥函式,同時將回調函式、檔案的內容作為引數傳遞。因此,不會阻塞或等待檔案I/O。這使得Node.js具有高度的可擴充套件性,因為它可以處理大量請求而無需等待任何函式返回結果。

阻塞程式碼示例

建立一個名為input.txt的文字檔案,內容如下 −

Tutorials Point is giving self learning content
to teach the world in simple and easy way!!!!!

建立一個名為main.js的js檔案,程式碼如下 −

var fs = require("fs");
var data = fs.readFileSync('input.txt');

console.log(data.toString());
console.log("Program Ended");

現在執行main.js檢視結果 −

$ node main.js

驗證輸出。

Tutorials Point is giving self learning content
to teach the world in simple and easy way!!!!!
Program Ended

非阻塞程式碼示例

建立一個名為input.txt的文字檔案,內容如下。

Tutorials Point is giving self learning content
to teach the world in simple and easy way!!!!!

更新main.js,使其包含以下程式碼 −

var fs = require("fs");

fs.readFile('input.txt', function (err, data) {
   if (err) return console.error(err);
   console.log(data.toString());
});

console.log("Program Ended");

現在執行main.js檢視結果 −

$ node main.js

驗證輸出。

Program Ended
Tutorials Point is giving self learning content
to teach the world in simple and easy way!!!!!

這兩個示例解釋了阻塞和非阻塞呼叫的概念。

  • 第一個示例表明,程式會阻塞,直到讀取檔案,然後才繼續結束程式。

  • 第二個示例表明,程式不會等待檔案讀取並繼續列印“程式結束”,同時,程式在不阻塞的情況下繼續讀取檔案。

因此,阻塞程式的執行非常順序。從程式設計的角度來看,實現邏輯更容易,但非阻塞程式的執行並非順序的。如果程式需要使用任何要處理的資料,則應將其儲存在同一個塊中以使其順序執行。

Node.js - 事件迴圈

Node.js是一個單執行緒應用程式,但它可以透過事件回撥的概念來支援併發。Node.js的每個API都是非同步的,並且是單執行緒的,它們使用非同步函式呼叫來維護併發。Node使用觀察者模式。Node執行緒保持一個事件迴圈,每當任務完成時,它就會觸發相應的事件,該事件會向事件監聽器函式發出執行訊號。

事件驅動程式設計

Node.js大量使用事件,這也是Node.js與其他類似技術相比速度很快的原因之一。Node啟動其伺服器後,它只需初始化其變數,宣告函式,然後簡單地等待事件發生。

在事件驅動的應用程式中,通常有一個主迴圈監聽事件,然後在檢測到這些事件之一時觸發回撥函式。

Event Loop

雖然事件看起來與回撥非常相似,但區別在於回撥函式是在非同步函式返回其結果時呼叫的,而事件處理基於觀察者模式。監聽事件的函式充當觀察者。每當觸發事件時,其監聽器函式就會開始執行。Node.js透過events模組和EventEmitter類提供了多個內建事件,它們用於繫結事件和事件監聽器,如下所示 −

// Import events module
var events = require('events');

// Create an eventEmitter object
var eventEmitter = new events.EventEmitter();

以下是將事件處理程式與事件繫結的語法 −

// Bind event and event  handler as follows
eventEmitter.on('eventName', eventHandler);

我們可以透過程式設計方式觸發事件,如下所示 −

// Fire an event 
eventEmitter.emit('eventName');

示例

建立一個名為main.js的js檔案,程式碼如下 −

// Import events module
var events = require('events');

// Create an eventEmitter object
var eventEmitter = new events.EventEmitter();

// Create an event handler as follows
var connectHandler = function connected() {
   console.log('connection succesful.');
  
   // Fire the data_received event 
   eventEmitter.emit('data_received');
}

// Bind the connection event with the handler
eventEmitter.on('connection', connectHandler);
 
// Bind the data_received event with the anonymous function
eventEmitter.on('data_received', function() {
   console.log('data received succesfully.');
});

// Fire the connection event 
eventEmitter.emit('connection');

console.log("Program Ended.");

現在讓我們嘗試執行上述程式並檢查其輸出 −

$ node main.js

它應該產生以下結果 −

connection successful.
data received successfully.
Program Ended.

Node應用程式如何工作?

在Node應用程式中,任何非同步函式都接受回撥作為最後一個引數,回撥函式接受錯誤作為第一個引數。讓我們再次回顧之前的示例。建立一個名為input.txt的文字檔案,內容如下。

Tutorials Point is giving self learning content
to teach the world in simple and easy way!!!!!

建立一個名為main.js的js檔案,包含以下程式碼 −

var fs = require("fs");

fs.readFile('input.txt', function (err, data) {
   if (err) {
      console.log(err.stack);
      return;
   }
   console.log(data.toString());
});
console.log("Program Ended");

這裡fs.readFile()是一個非同步函式,其目的是讀取檔案。如果在讀取操作期間發生錯誤,則err物件將包含相應的錯誤,否則data將包含檔案的內容。readFile在讀取操作完成後將err和data傳遞給回撥函式,最後列印內容。

Program Ended
Tutorials Point is giving self learning content
to teach the world in simple and easy way!!!!!

Node.js - 事件發射器

Node中的許多物件都會發出事件,例如,net.Server在每次對等體連線到它時都會發出一個事件,fs.readStream在檔案開啟時會發出一個事件。所有發出事件的物件都是events.EventEmitter的例項。

EventEmitter類

正如我們在上一節中看到的,EventEmitter類位於events模組中。可以透過以下程式碼訪問它 −

// Import events module
var events = require('events');

// Create an eventEmitter object
var eventEmitter = new events.EventEmitter();

當EventEmitter例項遇到任何錯誤時,它會發出'error'事件。新增新的監聽器時,會觸發'newListener'事件;移除監聽器時,會觸發'removeListener'事件。

EventEmitter提供了多個屬性,例如onemiton屬性用於將函式與事件繫結,emit用於觸發事件。

方法

序號 方法和描述
1

addListener(event, listener)

為指定的事件在監聽器陣列的末尾新增一個監聽器。不會進行任何檢查以檢視是否已新增監聽器。多次呼叫傳遞相同組合的事件和監聽器將導致監聽器被多次新增。返回發射器,因此可以連結呼叫。

2

on(event, listener)

為指定的事件在監聽器陣列的末尾新增一個監聽器。不會進行任何檢查以檢視是否已新增監聽器。多次呼叫傳遞相同組合的事件和監聽器將導致監聽器被多次新增。返回發射器,因此可以連結呼叫。

3

once(event, listener)

向事件新增一次性監聽器。此監聽器僅在下一次觸發事件時呼叫,之後將其刪除。返回發射器,因此可以連結呼叫。

4

removeListener(event, listener)

從指定事件的監聽器陣列中移除一個監聽器。注意 − 它會更改監聽器陣列中監聽器後面的陣列索引。removeListener 最多隻會從監聽器陣列中移除一個監聽器例項。如果任何單個監聽器已多次新增到指定事件的監聽器陣列中,則必須多次呼叫 removeListener 才能移除每個例項。返回 emitter,以便可以連結呼叫。

5

removeAllListeners([event])

移除所有監聽器,或指定事件的監聽器。移除在程式碼其他地方新增的監聽器不是一個好主意,尤其是在您沒有建立的 emitter 上(例如套接字或檔案流)。返回 emitter,以便可以連結呼叫。

6

setMaxListeners(n)

預設情況下,如果為特定事件添加了超過 10 個監聽器,EventEmitters 將列印警告。這是一個有用的預設設定,有助於查詢記憶體洩漏。顯然,並非所有 Emitters 都應該限制為 10 個。此函式允許增加此限制。設定為零表示無限制。

7

listeners(event)

返回指定事件的監聽器陣列。

8

emit(event, [arg1], [arg2], [...])

按順序使用提供的引數執行每個監聽器。如果事件有監聽器,則返回 true,否則返回 false。

類方法

序號 方法和描述
1

listenerCount(emitter, event)

返回給定事件的監聽器數量。

事件

序號 事件與描述
1

newListener

  • 事件 − 字串:事件名稱

  • 監聽器 − 函式:事件處理程式函式

每當新增監聽器時都會發出此事件。觸發此事件時,監聽器可能尚未新增到該事件的監聽器陣列中。

2

removeListener

  • 事件 − 字串 事件名稱

  • 監聽器 − 函式 事件處理程式函式

每當有人移除監聽器時都會發出此事件。觸發此事件時,監聽器可能尚未從該事件的監聽器陣列中移除。

示例

建立一個名為 main.js 的 js 檔案,其中包含以下 Node.js 程式碼 −

var events = require('events');
var eventEmitter = new events.EventEmitter();

// listener #1
var listner1 = function listner1() {
   console.log('listner1 executed.');
}

// listener #2
var listner2 = function listner2() {
   console.log('listner2 executed.');
}

// Bind the connection event with the listner1 function
eventEmitter.addListener('connection', listner1);

// Bind the connection event with the listner2 function
eventEmitter.on('connection', listner2);

var eventListeners = require('events').EventEmitter.listenerCount
   (eventEmitter,'connection');
console.log(eventListeners + " Listner(s) listening to connection event");

// Fire the connection event 
eventEmitter.emit('connection');

// Remove the binding of listner1 function
eventEmitter.removeListener('connection', listner1);
console.log("Listner1 will not listen now.");

// Fire the connection event 
eventEmitter.emit('connection');

eventListeners = require('events').EventEmitter.listenerCount(eventEmitter,'connection');
console.log(eventListeners + " Listner(s) listening to connection event");

console.log("Program Ended.");

現在執行main.js檢視結果 −

$ node main.js

驗證輸出。

2 Listner(s) listening to connection event
listner1 executed.
listner2 executed.
Listner1 will not listen now.
listner2 executed.
1 Listner(s) listening to connection event
Program Ended.

Node.js - 緩衝區

純 JavaScript 支援 Unicode,但不支援二進位制資料。在處理 TCP 流或檔案系統時,需要處理八位位元組流。Node 提供 Buffer 類,該類提供例項來儲存原始資料,類似於整數陣列,但對應於 V8 堆之外的原始記憶體分配。

Buffer 類是一個全域性類,無需匯入 buffer 模組即可在應用程式中訪問。

建立緩衝區

Node Buffer 可以透過多種方式構建。

方法 1

以下是建立10個八位位元組的未初始化緩衝區的語法 −

var buf = new Buffer(10);

方法 2

以下是根據給定陣列建立緩衝區的語法 −

var buf = new Buffer([10, 20, 30, 40, 50]);

方法 3

以下是根據給定字串和可選編碼型別建立緩衝區的語法 −

var buf = new Buffer("Simply Easy Learning", "utf-8");

雖然“utf8”是預設編碼,但您可以使用以下任何編碼:“ascii”、“utf8”、“utf16le”、“ucs2”、“base64”或“hex”。

寫入緩衝區

語法

以下是寫入 Node Buffer 的方法的語法 −

buf.write(string[, offset][, length][, encoding])

引數

以下是所用引數的描述 −

  • 字串 − 要寫入緩衝區的字串資料。

  • 偏移量 − 開始寫入的緩衝區索引。預設值為 0。

  • 長度 − 要寫入的位元組數。預設為 buffer.length。

  • 編碼 − 要使用的編碼。'utf8' 是預設編碼。

返回值

此方法返回寫入的八位位元組數。如果緩衝區中沒有足夠的空間容納整個字串,它將寫入字串的一部分。

示例

buf = new Buffer(256);
len = buf.write("Simply Easy Learning");

console.log("Octets written : "+  len);

執行上述程式後,將產生以下結果 −

Octets written : 20

從緩衝區讀取

語法

以下是用於從 Node Buffer 讀取資料的方法的語法 −

buf.toString([encoding][, start][, end])

引數

以下是所用引數的描述 −

  • 編碼 − 要使用的編碼。'utf8' 是預設編碼。

  • 開始 − 開始讀取的起始索引,預設為 0。

  • 結束 − 結束讀取的結束索引,預設為整個緩衝區。

返回值

此方法使用指定的字元集編碼解碼並返回緩衝區資料中的字串。

示例

buf = new Buffer(26);
for (var i = 0 ; i < 26 ; i++) {
  buf[i] = i + 97;
}

console.log( buf.toString('ascii'));       // outputs: abcdefghijklmnopqrstuvwxyz
console.log( buf.toString('ascii',0,5));   // outputs: abcde
console.log( buf.toString('utf8',0,5));    // outputs: abcde
console.log( buf.toString(undefined,0,5)); // encoding defaults to 'utf8', outputs abcde

執行上述程式後,將產生以下結果 −

abcdefghijklmnopqrstuvwxyz
abcde
abcde
abcde

將緩衝區轉換為 JSON

語法

以下是將 Node Buffer 轉換為 JSON 物件的方法的語法 −

buf.toJSON()

返回值

此方法返回 Buffer 例項的 JSON 表示形式。

示例

var buf = new Buffer('Simply Easy Learning');
var json = buf.toJSON(buf);

console.log(json);

執行上述程式後,將產生以下結果 −

{ type: 'Buffer',
   data: 
   [ 
      83,
      105,
      109,
      112,
      108,
      121,
      32,
      69,
      97,
      115,
      121,
      32,
      76,
      101,
      97,
      114,
      110,
      105,
      110,
      103 
   ]
}

連線緩衝區

語法

以下是將 Node 緩衝區連線到單個 Node Buffer 的方法的語法 −

Buffer.concat(list[, totalLength])

引數

以下是所用引數的描述 −

  • 列表 − 要連線的 Buffer 物件的陣列列表。

  • 總長度 − 連線後緩衝區的總長度。

返回值

此方法返回一個 Buffer 例項。

示例

var buffer1 = new Buffer('TutorialsPoint ');
var buffer2 = new Buffer('Simply Easy Learning');
var buffer3 = Buffer.concat([buffer1,buffer2]);

console.log("buffer3 content: " + buffer3.toString());

執行上述程式後,將產生以下結果 −

buffer3 content: TutorialsPoint Simply Easy Learning

比較緩衝區

語法

以下是比較兩個 Node 緩衝區的方法的語法 −

buf.compare(otherBuffer);

引數

以下是所用引數的描述 −

  • otherBuffer − 將與buf比較的另一個緩衝區

返回值

返回一個數字,指示它在排序順序中是在 otherBuffer 之前、之後還是與之相同。

示例

var buffer1 = new Buffer('ABC');
var buffer2 = new Buffer('ABCD');
var result = buffer1.compare(buffer2);

if(result < 0) {
   console.log(buffer1 +" comes before " + buffer2);
} else if(result === 0) {
   console.log(buffer1 +" is same as " + buffer2);
} else {
   console.log(buffer1 +" comes after " + buffer2);
}

執行上述程式後,將產生以下結果 −

ABC comes before ABCD

複製緩衝區

語法

以下是複製節點緩衝區的方法的語法 −

buf.copy(targetBuffer[, targetStart][, sourceStart][, sourceEnd])

引數

以下是所用引數的描述 −

  • targetBuffer − 將複製緩衝區的 Buffer 物件。

  • targetStart − 數字,可選,預設值:0

  • sourceStart − 數字,可選,預設值:0

  • sourceEnd − 數字,可選,預設值:buffer.length

返回值

無返回值。即使目標記憶體區域與源記憶體區域重疊,也會將資料從此緩衝區的某個區域複製到目標緩衝區的某個區域。如果未定義,則 targetStart 和 sourceStart 引數預設為 0,而 sourceEnd 預設為 buffer.length。

示例

var buffer1 = new Buffer('ABC');

//copy a buffer
var buffer2 = new Buffer(3);
buffer1.copy(buffer2);
console.log("buffer2 content: " + buffer2.toString());

執行上述程式後,將產生以下結果 −

buffer2 content: ABC

切片緩衝區

語法

以下是獲取節點緩衝區的子緩衝區的方法的語法 −

buf.slice([start][, end])

引數

以下是所用引數的描述 −

  • 開始 − 數字,可選,預設值:0

  • 結束 − 數字,可選,預設值:buffer.length

返回值

返回一個新的緩衝區,它引用與舊緩衝區相同的記憶體,但透過起始(預設為 0)和結束(預設為 buffer.length)索引進行偏移和裁剪。負索引從緩衝區的末尾開始。

示例

var buffer1 = new Buffer('TutorialsPoint');

//slicing a buffer
var buffer2 = buffer1.slice(0,9);
console.log("buffer2 content: " + buffer2.toString());

執行上述程式後,將產生以下結果 −

buffer2 content: Tutorials

緩衝區長度

語法

以下是獲取節點緩衝區大小(以位元組為單位)的方法的語法 −

buf.length;

返回值

返回緩衝區的大小(以位元組為單位)。

示例

var buffer = new Buffer('TutorialsPoint');

//length of the buffer
console.log("buffer length: " + buffer.length);

執行上述程式後,將產生以下結果 −

buffer length: 14

方法參考

類方法

序號 方法和描述
1

Buffer.isEncoding(encoding)

如果編碼是一個有效的編碼引數,則返回true,否則返回false。

2

Buffer.isBuffer(obj)

測試obj是否是一個Buffer。

3

Buffer.byteLength(string[, encoding])

給出字串的實際位元組長度。encoding預設為'utf8'。它與String.prototype.length不同,因為String.prototype.length返回字串中字元的數量。

4

Buffer.concat(list[, totalLength])

返回一個緩衝區,它是將列表中所有緩衝區連線在一起的結果。

5

Buffer.compare(buf1, buf2)

與buf1.compare(buf2)相同。對於排序緩衝區陣列很有用。

Node.js - 流

什麼是流?

流是允許您以連續方式從源讀取資料或向目標寫入資料的物件。在Node.js中,有四種類型的流:

  • 可讀流 - 用於讀取操作的流。

  • 可寫流 - 用於寫入操作的流。

  • 雙工流 - 可用於讀取和寫入操作的流。

  • 轉換流 - 一種雙工流,其中輸出是基於輸入計算的。

每種型別的流都是一個EventEmitter例項,並在不同的時間例項中丟擲多個事件。例如,一些常用的事件是:

  • data - 當有資料可供讀取時觸發此事件。

  • end - 當沒有更多資料可讀取時觸發此事件。

  • error - 當接收或寫入資料時發生任何錯誤時觸發此事件。

  • finish - 當所有資料都已重新整理到底層系統時觸發此事件。

本教程提供了對流上常用操作的基本理解。

從流中讀取

建立一個名為input.txt的文字檔案,內容如下:

Tutorials Point is giving self learning content
to teach the world in simple and easy way!!!!!

建立一個名為main.js的js檔案,程式碼如下 −

var fs = require("fs");
var data = '';

// Create a readable stream
var readerStream = fs.createReadStream('input.txt');

// Set the encoding to be utf8. 
readerStream.setEncoding('UTF8');

// Handle stream events --> data, end, and error
readerStream.on('data', function(chunk) {
   data += chunk;
});

readerStream.on('end',function() {
   console.log(data);
});

readerStream.on('error', function(err) {
   console.log(err.stack);
});

console.log("Program Ended");

現在執行main.js檢視結果 −

$ node main.js

驗證輸出。

Program Ended
Tutorials Point is giving self learning content
to teach the world in simple and easy way!!!!!

寫入流

建立一個名為main.js的js檔案,程式碼如下 −

var fs = require("fs");
var data = 'Simply Easy Learning';

// Create a writable stream
var writerStream = fs.createWriteStream('output.txt');

// Write the data to stream with encoding to be utf8
writerStream.write(data,'UTF8');

// Mark the end of file
writerStream.end();

// Handle stream events --> finish, and error
writerStream.on('finish', function() {
   console.log("Write completed.");
});

writerStream.on('error', function(err) {
   console.log(err.stack);
});

console.log("Program Ended");

現在執行main.js檢視結果 −

$ node main.js

驗證輸出。

Program Ended
Write completed.

現在開啟您當前目錄中建立的output.txt;它應該包含以下內容:

Simply Easy Learning

管道流

管道是一種機制,我們將一個流的輸出作為另一個流的輸入。它通常用於從一個流獲取資料並將該流的輸出傳遞到另一個流。管道操作沒有限制。現在我們將展示一個從一個檔案讀取並將其寫入另一個檔案的管道示例。

建立一個名為main.js的js檔案,程式碼如下 −

var fs = require("fs");

// Create a readable stream
var readerStream = fs.createReadStream('input.txt');

// Create a writable stream
var writerStream = fs.createWriteStream('output.txt');

// Pipe the read and write operations
// read input.txt and write data to output.txt
readerStream.pipe(writerStream);

console.log("Program Ended");

現在執行main.js檢視結果 −

$ node main.js

驗證輸出。

Program Ended

現在開啟您當前目錄中建立的output.txt;它應該包含以下內容:

Tutorials Point is giving self learning content
to teach the world in simple and easy way!!!!!

流的鏈式操作

鏈式操作是一種將一個流的輸出連線到另一個流並建立多個流操作鏈的機制。它通常與管道操作一起使用。現在我們將使用管道和鏈式操作先壓縮一個檔案,然後再解壓縮同一個檔案。

建立一個名為main.js的js檔案,程式碼如下 −

var fs = require("fs");
var zlib = require('zlib');

// Compress the file input.txt to input.txt.gz
fs.createReadStream('input.txt')
   .pipe(zlib.createGzip())
   .pipe(fs.createWriteStream('input.txt.gz'));
  
console.log("File Compressed.");

現在執行main.js檢視結果 −

$ node main.js

驗證輸出。

File Compressed.

您會發現input.txt已被壓縮,並在當前目錄中建立了一個名為input.txt.gz的檔案。現在讓我們嘗試使用以下程式碼解壓縮同一個檔案:

var fs = require("fs");
var zlib = require('zlib');

// Decompress the file input.txt.gz to input.txt
fs.createReadStream('input.txt.gz')
   .pipe(zlib.createGunzip())
   .pipe(fs.createWriteStream('input.txt'));
  
console.log("File Decompressed.");

現在執行main.js檢視結果 −

$ node main.js

驗證輸出。

File Decompressed.

Node.js - 檔案系統

Node使用圍繞標準POSIX函式的簡單包裝器實現檔案I/O。Node檔案系統(fs)模組可以使用以下語法匯入:

var fs = require("fs")

同步與非同步

fs 模組中的每個方法都具有同步和非同步形式。非同步方法將最後一個引數作為完成函式回撥,回撥函式的第一個引數作為錯誤。最好使用非同步方法而不是同步方法,因為前者在執行期間不會阻塞程式,而後者會。

示例

建立一個名為input.txt的文字檔案,內容如下 −

Tutorials Point is giving self learning content
to teach the world in simple and easy way!!!!!

讓我們建立一個名為main.js的 js 檔案,其中包含以下程式碼:

var fs = require("fs");

// Asynchronous read
fs.readFile('input.txt', function (err, data) {
   if (err) {
      return console.error(err);
   }
   console.log("Asynchronous read: " + data.toString());
});

// Synchronous read
var data = fs.readFileSync('input.txt');
console.log("Synchronous read: " + data.toString());

console.log("Program Ended");

現在執行main.js檢視結果 −

$ node main.js

驗證輸出。

Synchronous read: Tutorials Point is giving self learning content
to teach the world in simple and easy way!!!!!

Program Ended
Asynchronous read: Tutorials Point is giving self learning content
to teach the world in simple and easy way!!!!!

本章的以下部分提供了一組關於主要檔案 I/O 方法的良好示例。

開啟檔案

語法

以下是非同步模式下開啟檔案的語法:

fs.open(path, flags[, mode], callback)

引數

以下是所用引數的描述 −

  • path - 這是一個包含路徑的檔名字串。

  • flags - 標誌指示要開啟的檔案的行為。所有可能的值如下所示。

  • mode - 它設定檔案模式(許可權和粘滯位),但僅當檔案被建立時才設定。預設為 0666,可讀寫。

  • callback - 這是一個回撥函式,它獲取兩個引數 (err, fd)。

標誌

讀/寫操作的標誌:

序號 標誌 & 描述
1

r

開啟檔案以進行讀取。如果檔案不存在,則會發生異常。

2

r+

開啟檔案以進行讀寫。如果檔案不存在,則會發生異常。

3

rs

以同步模式開啟檔案進行讀取。

4

rs+

開啟檔案進行讀寫,要求作業系統以同步方式開啟它。關於謹慎使用此選項與 'rs' 的說明,請參見注釋。

5

w

開啟檔案以進行寫入。如果檔案不存在,則建立檔案;如果檔案存在,則截斷檔案。

6

wx

類似於 'w',但如果路徑存在則失敗。

7

w+

開啟檔案以進行讀寫。如果檔案不存在,則建立檔案;如果檔案存在,則截斷檔案。

8

wx+

類似於 'w+',但如果路徑存在則失敗。

9

a

開啟檔案以進行追加。如果檔案不存在,則建立檔案。

10

ax

類似於 'a',但如果路徑存在則失敗。

11

a+

開啟檔案以進行讀取和追加。如果檔案不存在,則建立檔案。

12

ax+

類似於 'a+',但如果路徑存在則失敗。

示例

讓我們建立一個名為main.js的 js 檔案,其中包含以下程式碼以開啟 input.txt 檔案進行讀寫。

var fs = require("fs");

// Asynchronous - Opening File
console.log("Going to open file!");
fs.open('input.txt', 'r+', function(err, fd) {
   if (err) {
      return console.error(err);
   }
   console.log("File opened successfully!");     
});

現在執行main.js檢視結果 −

$ node main.js

驗證輸出。

Going to open file!
File opened successfully!

獲取檔案資訊

語法

以下是獲取有關檔案資訊的方法的語法:

fs.stat(path, callback)

引數

以下是所用引數的描述 −

  • path - 這是一個包含路徑的檔名字串。

  • callback - 這是一個回撥函式,它獲取兩個引數 (err, stats),其中stats 是 fs.Stats 型別的物件,示例中列印如下。

除了示例中列印的重要屬性之外,fs.Stats 類中還提供了一些有用的方法,可用於檢查檔案型別。這些方法在下面的表格中給出。

序號 方法和描述
1

stats.isFile()

如果檔案型別是普通檔案,則返回 true。

2

stats.isDirectory()

如果檔案型別是目錄,則返回 true。

3

stats.isBlockDevice()

如果檔案型別是塊裝置,則返回 true。

4

stats.isCharacterDevice()

如果檔案型別是字元裝置,則返回 true。

5

stats.isSymbolicLink()

如果檔案型別是符號連結,則返回 true。

6

stats.isFIFO()

如果檔案型別是 FIFO,則返回 true。

7

stats.isSocket()

如果檔案型別是套接字,則返回 true。

示例

讓我們建立一個名為main.js的 js 檔案,其中包含以下程式碼:

var fs = require("fs");

console.log("Going to get file info!");
fs.stat('input.txt', function (err, stats) {
   if (err) {
      return console.error(err);
   }
   console.log(stats);
   console.log("Got file info successfully!");
   
   // Check file type
   console.log("isFile ? " + stats.isFile());
   console.log("isDirectory ? " + stats.isDirectory());    
});

現在執行main.js檢視結果 −

$ node main.js

驗證輸出。

Going to get file info!
{ 
   dev: 1792,
   mode: 33188,
   nlink: 1,
   uid: 48,
   gid: 48,
   rdev: 0,
   blksize: 4096,
   ino: 4318127,
   size: 97,
   blocks: 8,
   atime: Sun Mar 22 2015 13:40:00 GMT-0500 (CDT),
   mtime: Sun Mar 22 2015 13:40:57 GMT-0500 (CDT),
   ctime: Sun Mar 22 2015 13:40:57 GMT-0500 (CDT) 
}
Got file info successfully!
isFile ? true
isDirectory ? false

寫入檔案

語法

以下是寫入檔案的方法之一的語法:

fs.writeFile(filename, data[, options], callback)

如果檔案已存在,此方法將覆蓋該檔案。如果要寫入現有檔案,則應使用另一種可用方法。

引數

以下是所用引數的描述 −

  • path - 這是一個包含路徑的檔名字串。

  • data - 這是要寫入檔案的字串或緩衝區。

  • options - 第三個引數是一個物件,它將包含 {encoding, mode, flag}。預設情況下,encoding 為 utf8,mode 為八進位制值 0666,flag 為 'w'。

  • callback - 這是一個回撥函式,它獲取一個引數 err,該引數在出現任何寫入錯誤時返回錯誤。

示例

讓我們建立一個名為main.js的 js 檔案,其中包含以下程式碼:

var fs = require("fs");

console.log("Going to write into existing file");
fs.writeFile('input.txt', 'Simply Easy Learning!', function(err) {
   if (err) {
      return console.error(err);
   }
   
   console.log("Data written successfully!");
   console.log("Let's read newly written data");
   
   fs.readFile('input.txt', function (err, data) {
      if (err) {
         return console.error(err);
      }
      console.log("Asynchronous read: " + data.toString());
   });
});

現在執行main.js檢視結果 −

$ node main.js

驗證輸出。

Going to write into existing file
Data written successfully!
Let's read newly written data
Asynchronous read: Simply Easy Learning!

讀取檔案

語法

以下是讀取檔案的方法之一的語法:

fs.read(fd, buffer, offset, length, position, callback)

此方法將使用檔案描述符讀取檔案。如果要直接使用檔名讀取檔案,則應使用另一種可用方法。

引數

以下是所用引數的描述 −

  • fd - 這是 fs.open() 返回的檔案描述符。

  • buffer - 這是將資料寫入到的緩衝區。

  • offset - 這是在緩衝區中開始寫入的偏移量。

  • length - 這是一個整數,指定要讀取的位元組數。

  • position - 這是一個整數,指定從檔案的何處開始讀取。如果 position 為 null,則將從當前檔案位置讀取資料。

  • callback - 這是一個回撥函式,它獲取三個引數:(err, bytesRead, buffer)。

示例

讓我們建立一個名為main.js的 js 檔案,其中包含以下程式碼:

var fs = require("fs");
var buf = new Buffer(1024);

console.log("Going to open an existing file");
fs.open('input.txt', 'r+', function(err, fd) {
   if (err) {
      return console.error(err);
   }
   console.log("File opened successfully!");
   console.log("Going to read the file");
   
   fs.read(fd, buf, 0, buf.length, 0, function(err, bytes){
      if (err){
         console.log(err);
      }
      console.log(bytes + " bytes read");
      
      // Print only read bytes to avoid junk.
      if(bytes > 0){
         console.log(buf.slice(0, bytes).toString());
      }
   });
});

現在執行main.js檢視結果 −

$ node main.js

驗證輸出。

Going to open an existing file
File opened successfully!
Going to read the file
97 bytes read
Tutorials Point is giving self learning content
to teach the world in simple and easy way!!!!!

關閉檔案

語法

以下是關閉已開啟檔案的語法:

fs.close(fd, callback)

引數

以下是所用引數的描述 −

  • fd - 這是檔案 fs.open() 方法返回的檔案描述符。

  • callback - 這是一個回撥函式,除了可能的異常之外,沒有其他引數傳遞給完成回撥。

示例

讓我們建立一個名為main.js的 js 檔案,其中包含以下程式碼:

var fs = require("fs");
var buf = new Buffer(1024);

console.log("Going to open an existing file");
fs.open('input.txt', 'r+', function(err, fd) {
   if (err) {
      return console.error(err);
   }
   console.log("File opened successfully!");
   console.log("Going to read the file");
   
   fs.read(fd, buf, 0, buf.length, 0, function(err, bytes) {
      if (err) {
         console.log(err);
      }

      // Print only read bytes to avoid junk.
      if(bytes > 0) {
         console.log(buf.slice(0, bytes).toString());
      }

      // Close the opened file.
      fs.close(fd, function(err) {
         if (err) {
            console.log(err);
         } 
         console.log("File closed successfully.");
      });
   });
});

現在執行main.js檢視結果 −

$ node main.js

驗證輸出。

Going to open an existing file
File opened successfully!
Going to read the file
Tutorials Point is giving self learning content
to teach the world in simple and easy way!!!!!

File closed successfully.

截斷檔案

語法

以下是截斷已開啟檔案的方法的語法:

fs.ftruncate(fd, len, callback)

引數

以下是所用引數的描述 −

  • fd - 這是 fs.open() 返回的檔案描述符。

  • len - 這是檔案截斷後的長度。

  • callback - 這是一個回撥函式,除了可能的異常之外,沒有其他引數傳遞給完成回撥。

示例

讓我們建立一個名為main.js的 js 檔案,其中包含以下程式碼:

var fs = require("fs");
var buf = new Buffer(1024);

console.log("Going to open an existing file");
fs.open('input.txt', 'r+', function(err, fd) {
   if (err) {
      return console.error(err);
   }
   console.log("File opened successfully!");
   console.log("Going to truncate the file after 10 bytes");
   
   // Truncate the opened file.
   fs.ftruncate(fd, 10, function(err) {
      if (err) {
         console.log(err);
      } 
      console.log("File truncated successfully.");
      console.log("Going to read the same file"); 
      
      fs.read(fd, buf, 0, buf.length, 0, function(err, bytes){
         if (err) {
            console.log(err);
         }

         // Print only read bytes to avoid junk.
         if(bytes > 0) {
            console.log(buf.slice(0, bytes).toString());
         }

         // Close the opened file.
         fs.close(fd, function(err) {
            if (err) {
               console.log(err);
            } 
            console.log("File closed successfully.");
         });
      });
   });
});

現在執行main.js檢視結果 −

$ node main.js

驗證輸出。

Going to open an existing file
File opened successfully!
Going to truncate the file after 10 bytes
File truncated successfully.
Going to read the same file
Tutorials 
File closed successfully.

刪除檔案

語法

以下是刪除檔案的方法的語法:

fs.unlink(path, callback)

引數

以下是所用引數的描述 −

  • path - 這是一個包含路徑的檔名。

  • callback - 這是一個回撥函式,除了可能的異常之外,沒有其他引數傳遞給完成回撥。

示例

讓我們建立一個名為main.js的 js 檔案,其中包含以下程式碼:

var fs = require("fs");

console.log("Going to delete an existing file");
fs.unlink('input.txt', function(err) {
   if (err) {
      return console.error(err);
   }
   console.log("File deleted successfully!");
});

現在執行main.js檢視結果 −

$ node main.js

驗證輸出。

Going to delete an existing file
File deleted successfully!

建立目錄

語法

以下是建立目錄的方法的語法:

fs.mkdir(path[, mode], callback)

引數

以下是所用引數的描述 −

  • path - 這是一個包含路徑的目錄名。

  • mode - 這是要設定的目錄許可權。預設為 0777。

  • callback - 這是一個回撥函式,除了可能的異常之外,沒有其他引數傳遞給完成回撥。

示例

讓我們建立一個名為main.js的 js 檔案,其中包含以下程式碼:

var fs = require("fs");

console.log("Going to create directory /tmp/test");
fs.mkdir('/tmp/test',function(err) {
   if (err) {
      return console.error(err);
   }
   console.log("Directory created successfully!");
});

現在執行main.js檢視結果 −

$ node main.js

驗證輸出。

Going to create directory /tmp/test
Directory created successfully!

讀取目錄

語法

以下是讀取目錄的方法的語法:

fs.readdir(path, callback)

引數

以下是所用引數的描述 −

  • path - 這是一個包含路徑的目錄名。

  • callback - 這是一個回撥函式,它獲取兩個引數 (err, files),其中 files 是目錄中檔名的陣列,不包括 '.' 和 '..'。

示例

讓我們建立一個名為main.js的 js 檔案,其中包含以下程式碼:

var fs = require("fs");

console.log("Going to read directory /tmp");
fs.readdir("/tmp/",function(err, files) {
   if (err) {
      return console.error(err);
   }
   files.forEach( function (file) {
      console.log( file );
   });
});

現在執行main.js檢視結果 −

$ node main.js

驗證輸出。

Going to read directory /tmp
ccmzx99o.out
ccyCSbkF.out
employee.ser
hsperfdata_apache
test
test.txt

刪除目錄

語法

以下是刪除目錄的方法的語法:

fs.rmdir(path, callback)

引數

以下是所用引數的描述 −

  • path - 這是一個包含路徑的目錄名。

  • callback - 這是一個回撥函式,除了可能的異常之外,沒有其他引數傳遞給完成回撥。

示例

讓我們建立一個名為main.js的 js 檔案,其中包含以下程式碼:

var fs = require("fs");

console.log("Going to delete directory /tmp/test");
fs.rmdir("/tmp/test",function(err) {
   if (err) {
      return console.error(err);
   }
   console.log("Going to read directory /tmp");
   
   fs.readdir("/tmp/",function(err, files) {
      if (err) {
         return console.error(err);
      }
      files.forEach( function (file) {
         console.log( file );
      });
   });
});

現在執行main.js檢視結果 −

$ node main.js

驗證輸出。

Going to read directory /tmp
ccmzx99o.out
ccyCSbkF.out
employee.ser
hsperfdata_apache
test.txt

方法參考

Node.js - 全域性物件

Node.js 全域性物件本質上是全域性的,它們在所有模組中都可用。我們不需要在應用程式中包含這些物件,而可以直接使用它們。這些物件是模組、函式、字串和物件本身,如下所述。

__filename

__filename 代表正在執行的程式碼的檔名。這是此程式碼檔案的已解析絕對路徑。對於主程式,這不一定與命令列中使用的檔名相同。模組內部的值是該模組檔案的路徑。

示例

建立一個名為main.js的js檔案,程式碼如下 −

// Let's try to print the value of __filename

console.log( __filename );

現在執行main.js檢視結果 −

$ node main.js

根據程式的位置,它將列印主檔名如下:

/web/com/1427091028_21099/main.js

__dirname

__dirname 代表當前正在執行的指令碼所在的目錄的名稱。

示例

建立一個名為main.js的js檔案,程式碼如下 −

// Let's try to print the value of __dirname

console.log( __dirname );

現在執行main.js檢視結果 −

$ node main.js

根據程式的位置,它將列印當前目錄名稱如下:

/web/com/1427091028_21099

setTimeout(cb, ms)

setTimeout(cb, ms) 全域性函式用於在至少 ms 毫秒後執行回撥 cb。實際延遲取決於外部因素,例如作業系統計時器粒度和系統負載。計時器不能超過 24.8 天。

此函式返回一個不透明值,該值表示可用於清除計時器的計時器。

示例

建立一個名為main.js的js檔案,程式碼如下 −

function printHello() {
   console.log( "Hello, World!");
}

// Now call above function after 2 seconds
setTimeout(printHello, 2000);

現在執行main.js檢視結果 −

$ node main.js

驗證輸出是否在稍後延遲後列印。

Hello, World!

clearTimeout(t)

clearTimeout(t) 全域性函式用於停止之前使用 setTimeout() 建立的計時器。這裡 t 是 setTimeout() 函式返回的計時器。

示例

建立一個名為main.js的js檔案,程式碼如下 −

function printHello() {
   console.log( "Hello, World!");
}

// Now call above function after 2 seconds
var t = setTimeout(printHello, 2000);

// Now clear the timer
clearTimeout(t);

現在執行main.js檢視結果 −

$ node main.js

驗證輸出,您將找不到任何列印的內容。

setInterval(cb, ms)

setInterval(cb, ms) 全域性函式用於在至少 ms 毫秒後重復執行回撥 cb。實際延遲取決於外部因素,例如作業系統計時器粒度和系統負載。計時器不能超過 24.8 天。

此函式返回一個不透明值,該值表示可使用函式 clearInterval(t) 清除計時器的計時器。

示例

建立一個名為main.js的js檔案,程式碼如下 −

function printHello() {
   console.log( "Hello, World!");
}

// Now call above function after 2 seconds
setInterval(printHello, 2000);

現在執行main.js檢視結果 −

$ node main.js

上述程式將在每 2 秒後執行 printHello()。由於系統限制。

全域性物件

下表列出了我們在應用程式中經常使用的其他物件。有關更多詳細資訊,您可以參考官方文件。

序號 模組名稱和描述
1 Console

用於在 stdout 和 stderr 上列印資訊。

2 Process

用於獲取當前程序的資訊。提供與程序活動相關的多個事件。

Node.js - 實用工具模組

Node.js 模組庫中提供了一些實用程式模組。這些模組非常常見,在開發任何基於 Node 的應用程式時經常使用。

序號 模組名稱和描述
1 OS 模組

提供基本的與作業系統相關的實用程式函式。

2 Path 模組

提供用於處理和轉換檔案路徑的實用程式。

3 Net 模組

提供伺服器和客戶端作為流。充當網路包裝器。

4 DNS 模組

提供執行實際 DNS 查詢以及使用底層作業系統名稱解析功能的函式。

5 Domain 模組

提供將多個不同的 I/O 操作作為單個組處理的方法。

Node.js - Web 模組

什麼是 Web 伺服器?

Web 伺服器是一個軟體應用程式,它處理 HTTP 客戶端(如 Web 瀏覽器)傳送的 HTTP 請求,並響應客戶端的請求返回網頁。Web 伺服器通常提供 html 文件以及影像、樣式表和指令碼。

大多數 Web 伺服器都支援伺服器端指令碼,使用指令碼語言或將任務重定向到應用程式伺服器,該伺服器從資料庫檢索資料並執行復雜的邏輯,然後透過 Web 伺服器將結果傳送到 HTTP 客戶端。

Apache Web 伺服器是最常用的 Web 伺服器之一。它是一個開源專案。

Web 應用程式架構

Web 應用程式通常分為四個層:

Web Architecture
  • 客戶端 - 此層包含 Web 瀏覽器、移動瀏覽器或可以向 Web 伺服器發出 HTTP 請求的應用程式。

  • 伺服器 - 此層包含可以攔截客戶端發出的請求並將響應傳遞給它們的 Web 伺服器。

  • 業務 - 此層包含 Web 伺服器用來執行所需處理的應用程式伺服器。此層透過資料庫或某些外部程式與資料層互動。

  • 資料 - 此層包含資料庫或任何其他資料來源。

使用 Node 建立 Web 伺服器

Node.js 提供了一個 http 模組,可用於建立伺服器的 HTTP 客戶端。以下是偵聽 8081 埠的 HTTP 伺服器的最小結構。

建立一個名為 server.js 的 js 檔案:

檔案:server.js

var http = require('http');
var fs = require('fs');
var url = require('url');

// Create a server
http.createServer( function (request, response) {  
   // Parse the request containing file name
   var pathname = url.parse(request.url).pathname;
   
   // Print the name of the file for which request is made.
   console.log("Request for " + pathname + " received.");
   
   // Read the requested file content from file system
   fs.readFile(pathname.substr(1), function (err, data) {
      if (err) {
         console.log(err);
         
         // HTTP Status: 404 : NOT FOUND
         // Content Type: text/plain
         response.writeHead(404, {'Content-Type': 'text/html'});
      } else {	
         //Page found	  
         // HTTP Status: 200 : OK
         // Content Type: text/plain
         response.writeHead(200, {'Content-Type': 'text/html'});	
         
         // Write the content of the file to response body
         response.write(data.toString());		
      }
      
      // Send the response body 
      response.end();
   });   
}).listen(8081);

// Console will print the message
console.log('Server running at http://127.0.0.1:8081/');

接下來,讓我們在建立 server.js 的同一目錄中建立名為 index.htm 的以下 html 檔案。

檔案:index.htm

<html>
   <head>
      <title>Sample Page</title>
   </head>
   
   <body>
      Hello World!
   </body>
</html>

現在讓我們執行 server.js 以檢視結果:

$ node server.js

驗證輸出。

Server running at http://127.0.0.1:8081/

向 Node.js 伺服器發出請求

在任何瀏覽器中開啟 http://127.0.0.1:8081/index.htm 以檢視以下結果。

First Server Application

驗證伺服器端的輸出。

Server running at http://127.0.0.1:8081/
Request for /index.htm received.

使用 Node 建立 Web 客戶端

可以使用 http 模組建立 Web 客戶端。讓我們檢查以下示例。

建立一個名為 client.js 的 js 檔案:

檔案:client.js

var http = require('http');

// Options to be used by request 
var options = {
   host: 'localhost',
   port: '8081',
   path: '/index.htm'  
};

// Callback function is used to deal with response
var callback = function(response) {
   // Continuously update stream with data
   var body = '';
   response.on('data', function(data) {
      body += data;
   });
   
   response.on('end', function() {
      // Data received completely.
      console.log(body);
   });
}
// Make a request to the server
var req = http.request(options, callback);
req.end();

現在從與 server.js 不同的命令終端執行 client.js 以檢視結果:

$ node client.js

驗證輸出。

<html>
   <head>
      <title>Sample Page</title>
   </head>
   
   <body>
      Hello World!
   </body>
</html>

驗證伺服器端的輸出。

Server running at http://127.0.0.1:8081/
Request for /index.htm received.

Node.js - Express 框架

Express 概述

Express 是一個最小且靈活的 Node.js Web 應用程式框架,它提供了一套強大的功能來開發 Web 和移動應用程式。它有助於快速開發基於 Node 的 Web 應用程式。以下是 Express 框架的一些核心功能:

  • 允許設定中介軟體以響應 HTTP 請求。

  • 定義路由表,用於根據 HTTP 方法和 URL 執行不同的操作。

  • 允許根據向模板傳遞引數動態渲染 HTML 頁面。

安裝 Express

首先,使用 NPM 全域性安裝 Express 框架,以便可以使用 node 終端來建立 Web 應用程式。

$ npm install express --save

上述命令將安裝儲存在 node_modules 目錄中,並在 node_modules 內建立 express 目錄。您應該與 express 一起安裝以下重要模組:

  • body-parser - 這是一個用於處理 JSON、Raw、Text 和 URL 編碼表單資料的 node.js 中介軟體。

  • cookie-parser - 解析 Cookie 標頭並將 req.cookies 填充為一個以 Cookie 名稱為鍵的物件。

  • multer - 這是一個用於處理 multipart/form-data 的 node.js 中介軟體。

$ npm install body-parser --save
$ npm install cookie-parser --save
$ npm install multer --save

Hello world 示例

以下是一個非常基本的 Express 應用程式,它啟動一個伺服器並在 8081 埠上偵聽連線。此應用程式對主頁的請求返回 Hello World!。對於其他所有路徑,它將返回 404 Not Found。

var express = require('express');
var app = express();

app.get('/', function (req, res) {
   res.send('Hello World');
})

var server = app.listen(8081, function () {
   var host = server.address().address
   var port = server.address().port
   
   console.log("Example app listening at http://%s:%s", host, port)
})

將以上程式碼儲存到名為 server.js 的檔案中,並使用以下命令執行它。

$ node server.js

您將看到以下輸出:

Example app listening at http://0.0.0.0:8081

在任何瀏覽器中開啟 http://127.0.0.1:8081/ 以檢視以下結果。

First Application

請求和響應

Express 應用程式使用回撥函式,其引數是 requestresponse 物件。

app.get('/', function (req, res) {
   // --
})
  • 請求物件 - 請求物件表示 HTTP 請求,並具有請求查詢字串、引數、主體、HTTP 標頭等的屬性。

  • 響應物件 - 響應物件表示 Express 應用程式在收到 HTTP 請求時傳送的 HTTP 響應。

您可以列印 reqres 物件,它們提供了許多與 HTTP 請求和響應相關的資訊,包括 Cookie、會話、URL 等。

基本路由

我們已經看到了一個為主頁提供 HTTP 請求的基本應用程式。路由是指確定應用程式如何響應客戶端對特定端點的請求,端點是 URI(或路徑)和特定的 HTTP 請求方法(GET、POST 等)。

我們將擴充套件我們的“Hello World”程式,使其能夠處理更多型別的HTTP請求。

var express = require('express');
var app = express();

// This responds with "Hello World" on the homepage
app.get('/', function (req, res) {
   console.log("Got a GET request for the homepage");
   res.send('Hello GET');
})

// This responds a POST request for the homepage
app.post('/', function (req, res) {
   console.log("Got a POST request for the homepage");
   res.send('Hello POST');
})

// This responds a DELETE request for the /del_user page.
app.delete('/del_user', function (req, res) {
   console.log("Got a DELETE request for /del_user");
   res.send('Hello DELETE');
})

// This responds a GET request for the /list_user page.
app.get('/list_user', function (req, res) {
   console.log("Got a GET request for /list_user");
   res.send('Page Listing');
})

// This responds a GET request for abcd, abxcd, ab123cd, and so on
app.get('/ab*cd', function(req, res) {   
   console.log("Got a GET request for /ab*cd");
   res.send('Page Pattern Match');
})

var server = app.listen(8081, function () {
   var host = server.address().address
   var port = server.address().port
   
   console.log("Example app listening at http://%s:%s", host, port)
})

將以上程式碼儲存到名為 server.js 的檔案中,並使用以下命令執行它。

$ node server.js

您將看到以下輸出:

Example app listening at http://0.0.0.0:8081

現在您可以嘗試不同的請求,訪問 http://127.0.0.1:8081 來檢視 server.js 生成的輸出。以下是一些螢幕截圖,顯示了不同 URL 的不同響應。

顯示 http://127.0.0.1:8081/list_user 的螢幕截圖

Second Application

顯示 http://127.0.0.1:8081/abcd 的螢幕截圖

Third Application

顯示 http://127.0.0.1:8081/abcdefg 的螢幕截圖

Fourth Application

服務靜態檔案

Express 提供了一個內建中介軟體 **express.static** 來服務靜態檔案,例如影像、CSS、JavaScript 等。

您只需將儲存靜態資源的目錄名稱傳遞給 **express.static** 中介軟體,即可開始直接服務這些檔案。例如,如果您將影像、CSS 和 JavaScript 檔案儲存在名為 public 的目錄中,您可以這樣做:

app.use(express.static('public'));

我們將一些影像儲存在 **public/images** 子目錄中,如下所示:

node_modules
server.js
public/
public/images
public/images/logo.png

讓我們修改“Hello World”應用程式,新增處理靜態檔案的功能。

var express = require('express');
var app = express();

app.use(express.static('public'));

app.get('/', function (req, res) {
   res.send('Hello World');
})

var server = app.listen(8081, function () {
   var host = server.address().address
   var port = server.address().port

   console.log("Example app listening at http://%s:%s", host, port)
})

將以上程式碼儲存到名為 server.js 的檔案中,並使用以下命令執行它。

$ node server.js

現在在任何瀏覽器中開啟 http://127.0.0.1:8081/images/logo.png 並檢視以下結果。

Fifth Application

GET 方法

這是一個簡單的示例,它使用 HTML FORM GET 方法傳遞兩個值。我們將使用 server.js 內部的 **process_get** 路由器來處理此輸入。

<html>
   <body>
      
      <form action = "http://127.0.0.1:8081/process_get" method = "GET">
         First Name: <input type = "text" name = "first_name">  <br>
         Last Name: <input type = "text" name = "last_name">
         <input type = "submit" value = "Submit">
      </form>
      
   </body>
</html>

讓我們將上述程式碼儲存在 index.htm 中,並修改 server.js 以處理主頁請求以及 HTML 表單傳送的輸入。

var express = require('express');
var app = express();

app.use(express.static('public'));
app.get('/index.htm', function (req, res) {
   res.sendFile( __dirname + "/" + "index.htm" );
})

app.get('/process_get', function (req, res) {
   // Prepare output in JSON format
   response = {
      first_name:req.query.first_name,
      last_name:req.query.last_name
   };
   console.log(response);
   res.end(JSON.stringify(response));
})

var server = app.listen(8081, function () {
   var host = server.address().address
   var port = server.address().port
   
   console.log("Example app listening at http://%s:%s", host, port)
})

使用 *http://127.0.0.1:8081/index.htm* 訪問 HTML 文件將生成以下表單:

First Name:
Last Name:

現在您可以輸入名字和姓氏,然後單擊提交按鈕檢視結果,它應該返回以下結果:

{"first_name":"John","last_name":"Paul"}

POST 方法

這是一個簡單的示例,它使用 HTML FORM POST 方法傳遞兩個值。我們將使用 server.js 內部的 **process_get** 路由器來處理此輸入。

<html>
   <body>
      
      <form action = "http://127.0.0.1:8081/process_post" method = "POST">
         First Name: <input type = "text" name = "first_name"> <br>
         Last Name: <input type = "text" name = "last_name">
         <input type = "submit" value = "Submit">
      </form>
      
   </body>
</html>

讓我們將上述程式碼儲存在 index.htm 中,並修改 server.js 以處理主頁請求以及 HTML 表單傳送的輸入。

var express = require('express');
var app = express();
var bodyParser = require('body-parser');

// Create application/x-www-form-urlencoded parser
var urlencodedParser = bodyParser.urlencoded({ extended: false })

app.use(express.static('public'));
app.get('/index.htm', function (req, res) {
   res.sendFile( __dirname + "/" + "index.htm" );
})

app.post('/process_post', urlencodedParser, function (req, res) {
   // Prepare output in JSON format
   response = {
      first_name:req.body.first_name,
      last_name:req.body.last_name
   };
   console.log(response);
   res.end(JSON.stringify(response));
})

var server = app.listen(8081, function () {
   var host = server.address().address
   var port = server.address().port
   
   console.log("Example app listening at http://%s:%s", host, port)
})

使用 *http://127.0.0.1:8081/index.htm* 訪問 HTML 文件將生成以下表單:

First Name:
Last Name:

現在您可以輸入名字和姓氏,然後單擊提交按鈕檢視以下結果:

{"first_name":"John","last_name":"Paul"}

檔案上傳

以下 HTML 程式碼建立了一個檔案上傳表單。此表單的 method 屬性設定為 **POST**,enctype 屬性設定為 **multipart/form-data**

<html>
   <head>
      <title>File Uploading Form</title>
   </head>

   <body>
      <h3>File Upload:</h3>
      Select a file to upload: <br />
      
      <form action = "http://127.0.0.1:8081/file_upload" method = "POST" 
         enctype = "multipart/form-data">
         <input type="file" name="file" size="50" />
         <br />
         <input type = "submit" value = "Upload File" />
      </form>
      
   </body>
</html>

讓我們將上述程式碼儲存在 index.htm 中,並修改 server.js 以處理主頁請求以及檔案上傳。

var express = require('express');
var app = express();
var fs = require("fs");

var bodyParser = require('body-parser');
var multer  = require('multer');

app.use(express.static('public'));
app.use(bodyParser.urlencoded({ extended: false }));
app.use(multer({ dest: '/tmp/'}));

app.get('/index.htm', function (req, res) {
   res.sendFile( __dirname + "/" + "index.htm" );
})

app.post('/file_upload', function (req, res) {
   console.log(req.files.file.name);
   console.log(req.files.file.path);
   console.log(req.files.file.type);
   var file = __dirname + "/" + req.files.file.name;
   
   fs.readFile( req.files.file.path, function (err, data) {
      fs.writeFile(file, data, function (err) {
         if( err ) {
            console.log( err );
            } else {
               response = {
                  message:'File uploaded successfully',
                  filename:req.files.file.name
               };
            }
         
         console.log( response );
         res.end( JSON.stringify( response ) );
      });
   });
})

var server = app.listen(8081, function () {
   var host = server.address().address
   var port = server.address().port
   
   console.log("Example app listening at http://%s:%s", host, port)
})

使用 *http://127.0.0.1:8081/index.htm* 訪問 HTML 文件將生成以下表單:

File Upload:
Select a file to upload: 

NOTE: This is just dummy form and would not work, but it must work at your server.

Cookie 管理

您可以向 Node.js 伺服器傳送 Cookie,該伺服器可以使用以下中介軟體選項進行處理。以下是一個簡單的示例,用於列印客戶端傳送的所有 Cookie。

var express      = require('express')
var cookieParser = require('cookie-parser')

var app = express()
app.use(cookieParser())

app.get('/', function(req, res) {
   console.log("Cookies: ", req.cookies)
})
app.listen(8081)

Node.js - RESTful API

什麼是 REST 架構?

REST 代表 REpresentational State Transfer(表述性狀態轉移)。REST 是基於 Web 標準的架構,並使用 HTTP 協議。它圍繞資源展開,其中每個元件都是一個資源,並且可以使用 HTTP 標準方法透過通用介面訪問資源。REST 最初由 Roy Fielding 於 2000 年提出。

REST 伺服器只需提供對資源的訪問,而 REST 客戶端則使用 HTTP 協議訪問和修改資源。這裡每個資源都由 URI/全域性 ID 標識。REST 使用各種表示來表示資源,例如文字、JSON、XML,但 JSON 是最流行的一種。

HTTP 方法

以下四種 HTTP 方法通常用於基於 REST 的架構。

  • **GET** - 用於提供對資源的只讀訪問。

  • **PUT** - 用於建立新資源。

  • **DELETE** - 用於刪除資源。

  • **POST** - 用於更新現有資源或建立新資源。

RESTful Web 服務

Web 服務是一組開放協議和標準,用於在應用程式或系統之間交換資料。用各種程式語言編寫的並在各種平臺上執行的軟體應用程式可以使用 Web 服務透過計算機網路(如網際網路)交換資料,其方式類似於在單個計算機上進行程序間通訊。這種互操作性(例如,Java 和 Python 之間的通訊,或 Windows 和 Linux 應用程式之間的通訊)是由於使用了開放標準。

基於 REST 架構的 Web 服務稱為 RESTful Web 服務。這些 Web 服務使用 HTTP 方法來實現 REST 架構的概念。RESTful Web 服務通常定義一個 URI(統一資源識別符號)服務,該服務提供資源表示(例如 JSON)和一組 HTTP 方法。

為圖書館建立 RESTful API

假設我們有一個基於 JSON 的使用者資料庫,在檔案 **users.json** 中有以下使用者:

{
   "user1" : {
      "name" : "mahesh",
      "password" : "password1",
      "profession" : "teacher",
      "id": 1
   },
   
   "user2" : {
      "name" : "suresh",
      "password" : "password2",
      "profession" : "librarian",
      "id": 2
   },
   
   "user3" : {
      "name" : "ramesh",
      "password" : "password3",
      "profession" : "clerk",
      "id": 3
   }
}

基於此資訊,我們將提供以下 RESTful API。

序號 URI HTTP 方法 POST 請求體 結果
1 listUsers GET 顯示所有使用者的列表。
2 addUser POST JSON 字串 新增新使用者的詳細資訊。
3 deleteUser DELETE JSON 字串 刪除現有使用者。
4 :id GET 顯示使用者的詳細資訊。

我將所有示例的大部分內容都以硬編碼的形式保留,假設您已經知道如何使用 Ajax 或簡單的表單資料從前端傳遞值,以及如何使用 express **Request** 物件處理它們。

列出使用者

讓我們在 server.js 檔案中使用以下程式碼實現我們的第一個 RESTful API **listUsers**:

server.js

var express = require('express');
var app = express();
var fs = require("fs");

app.get('/listUsers', function (req, res) {
   fs.readFile( __dirname + "/" + "users.json", 'utf8', function (err, data) {
      console.log( data );
      res.end( data );
   });
})

var server = app.listen(8081, function () {
   var host = server.address().address
   var port = server.address().port
   console.log("Example app listening at http://%s:%s", host, port)
})

現在嘗試使用任何 REST 客戶端在本地機器上使用 *URL: http://127.0.0.1:8081/listUsers* 和 *HTTP 方法: GET* 來訪問已定義的 API。這應該會產生以下結果:

當您將解決方案部署到生產環境時,您可以更改給定的 IP 地址。

{
   "user1" : {
      "name" : "mahesh",
      "password" : "password1",
      "profession" : "teacher",
      "id": 1
   },
   
   "user2" : {
      "name" : "suresh",
      "password" : "password2",
      "profession" : "librarian",
      "id": 2
   },
   
   "user3" : {
      "name" : "ramesh",
      "password" : "password3",
      "profession" : "clerk",
      "id": 3
   }
}

新增使用者

以下 API 將向您展示如何在列表中新增新使用者。以下是新使用者的詳細資訊:

user = {
   "user4" : {
      "name" : "mohit",
      "password" : "password4",
      "profession" : "teacher",
      "id": 4
   }
}

您可以使用 Ajax 呼叫以 JSON 格式接受相同的輸入,但出於教學目的,我們在這裡對其進行了硬編碼。以下是將新使用者新增到資料庫中的 **addUser** API:

server.js

var express = require('express');
var app = express();
var fs = require("fs");

var user = {
   "user4" : {
      "name" : "mohit",
      "password" : "password4",
      "profession" : "teacher",
      "id": 4
   }
}

app.post('/addUser', function (req, res) {
   // First read existing users.
   fs.readFile( __dirname + "/" + "users.json", 'utf8', function (err, data) {
      data = JSON.parse( data );
      data["user4"] = user["user4"];
      console.log( data );
      res.end( JSON.stringify(data));
   });
})

var server = app.listen(8081, function () {
   var host = server.address().address
   var port = server.address().port
   console.log("Example app listening at http://%s:%s", host, port)
})

現在嘗試使用任何 REST 客戶端在本地機器上使用 *URL: http://127.0.0.1:8081/addUser* 和 *HTTP 方法: POST* 來訪問已定義的 API。這應該會產生以下結果:

{
   "user1":{"name":"mahesh","password":"password1","profession":"teacher","id":1},
   "user2":{"name":"suresh","password":"password2","profession":"librarian","id":2},
   "user3":{"name":"ramesh","password":"password3","profession":"clerk","id":3},
   "user4":{"name":"mohit","password":"password4","profession":"teacher","id":4}
}

顯示詳細資訊

現在我們將實現一個 API,該 API 將使用使用者 ID 呼叫,並將顯示相應使用者的詳細資訊。

server.js

var express = require('express');
var app = express();
var fs = require("fs");

app.get('/:id', function (req, res) {
   // First read existing users.
   fs.readFile( __dirname + "/" + "users.json", 'utf8', function (err, data) {
      var users = JSON.parse( data );
      var user = users["user" + req.params.id] 
      console.log( user );
      res.end( JSON.stringify(user));
   });
})

var server = app.listen(8081, function () {
   var host = server.address().address
   var port = server.address().port
   console.log("Example app listening at http://%s:%s", host, port)
})

現在嘗試使用任何 REST 客戶端在本地機器上使用 *URL: http://127.0.0.1:8081/2* 和 *HTTP 方法: GET* 來訪問已定義的 API。這應該會產生以下結果:

{"name":"suresh","password":"password2","profession":"librarian","id":2}

刪除使用者

此 API 與 addUser API 非常相似,我們透過 req.body 接收輸入資料,然後根據使用者 ID 從資料庫中刪除該使用者。為了使我們的程式更簡單,我們假設我們將刪除 ID 為 2 的使用者。

server.js

var express = require('express');
var app = express();
var fs = require("fs");

var id = 2;

app.delete('/deleteUser', function (req, res) {
   // First read existing users.
   fs.readFile( __dirname + "/" + "users.json", 'utf8', function (err, data) {
      data = JSON.parse( data );
      delete data["user" + 2];
       
      console.log( data );
      res.end( JSON.stringify(data));
   });
})

var server = app.listen(8081, function () {
   var host = server.address().address
   var port = server.address().port
   console.log("Example app listening at http://%s:%s", host, port)
})

現在嘗試使用任何 REST 客戶端在本地機器上使用 *URL: http://127.0.0.1:8081/deleteUser* 和 *HTTP 方法: DELETE* 來訪問已定義的 API。這應該會產生以下結果:

{"user1":{"name":"mahesh","password":"password1","profession":"teacher","id":1},
"user3":{"name":"ramesh","password":"password3","profession":"clerk","id":3}}

Node.js - 應用程式擴充套件

Node.js 以單執行緒模式執行,但它使用事件驅動範例來處理併發。它還促進建立子程序,以利用基於多核 CPU 的系統上的並行處理。

子程序始終具有三個流 **child.stdin**、**child.stdout** 和 **child.stderr**,這些流可能與父程序的 stdio 流共享。

Node 提供了 **child_process** 模組,該模組有以下三種主要方法來建立子程序。

  • **exec** - child_process.exec 方法在 shell/控制檯中執行命令並緩衝輸出。

  • **spawn** - child_process.spawn 使用給定的命令啟動一個新程序。

  • **fork** - child_process.fork 方法是 spawn() 的一個特例,用於建立子程序。

exec() 方法

child_process.exec 方法在 shell 中執行命令並緩衝輸出。它具有以下簽名:

child_process.exec(command[, options], callback)

引數

以下是所用引數的描述 −

  • **command** (字串) 要執行的命令,帶空格分隔的引數

  • **options** (物件) 可能包含以下一個或多個選項:

    • **cwd** (字串) 子程序的當前工作目錄

    • **env** (物件) 環境鍵值對

    • **encoding** (字串) (預設值:'utf8')

    • **shell** (字串) 用於執行命令的 shell (UNIX 上預設為 '/bin/sh',Windows 上預設為 'cmd.exe',shell 應該理解 UNIX 上的 -c 開關或 Windows 上的 /s /c。在 Windows 上,命令列解析應該與 cmd.exe 相容。)

    • **timeout** (數字) (預設值:0)

    • **maxBuffer** (數字) (預設值:200*1024)

    • **killSignal** (字串) (預設值:'SIGTERM')

    • **uid** (數字) 設定程序的使用者身份。

    • **gid** (數字) 設定程序的組身份。

  • **callback** 當程序終止時,該函式將獲得三個引數 **error**、**stdout** 和 **stderr**,並使用輸出呼叫它們。

exec() 方法返回一個具有最大大小的緩衝區,並等待程序結束,並嘗試一次返回所有緩衝資料。

示例

讓我們建立兩個名為 support.js 和 master.js 的 js 檔案:

檔案:support.js

console.log("Child Process " + process.argv[2] + " executed." );

檔案:master.js

const fs = require('fs');
const child_process = require('child_process');

for(var i=0; i<3; i++) {
   var workerProcess = child_process.exec('node support.js '+i,function 
      (error, stdout, stderr) {
      
      if (error) {
         console.log(error.stack);
         console.log('Error code: '+error.code);
         console.log('Signal received: '+error.signal);
      }
      console.log('stdout: ' + stdout);
      console.log('stderr: ' + stderr);
   });

   workerProcess.on('exit', function (code) {
      console.log('Child process exited with exit code '+code);
   });
}

現在執行 master.js 檢視結果:

$ node master.js

驗證輸出。伺服器已啟動。

Child process exited with exit code 0
stdout: Child Process 1 executed.

stderr:
Child process exited with exit code 0
stdout: Child Process 0 executed.

stderr:
Child process exited with exit code 0
stdout: Child Process 2 executed.

spawn() 方法

child_process.spawn 方法使用給定的命令啟動一個新程序。它具有以下簽名:

child_process.spawn(command[, args][, options])

引數

以下是所用引數的描述 −

  • **command** (字串) 要執行的命令

  • **args** (陣列) 字串引數列表

  • **options** (物件) 可能包含以下一個或多個選項:

    • **cwd** (字串) 子程序的當前工作目錄。

    • **env** (物件) 環境鍵值對。

    • **stdio** (陣列) 字串 子程序的 stdio 配置。

    • **customFds** (陣列) 已棄用 子程序要使用的 stdio 檔案描述符。

    • **detached** (布林值) 子程序將成為程序組領導者。

    • **uid** (數字) 設定程序的使用者身份。

    • **gid** (數字) 設定程序的組身份。

spawn() 方法返回流 (stdout & stderr),當程序返回大量資料時應使用它。spawn() 在程序開始執行時就開始接收響應。

示例

建立兩個名為 support.js 和 master.js 的 js 檔案:

檔案:support.js

console.log("Child Process " + process.argv[2] + " executed." );

檔案:master.js

const fs = require('fs');
const child_process = require('child_process');
 
for(var i = 0; i<3; i++) {
   var workerProcess = child_process.spawn('node', ['support.js', i]);

   workerProcess.stdout.on('data', function (data) {
      console.log('stdout: ' + data);
   });

   workerProcess.stderr.on('data', function (data) {
      console.log('stderr: ' + data);
   });

   workerProcess.on('close', function (code) {
      console.log('child process exited with code ' + code);
   });
}

現在執行 master.js 檢視結果:

$ node master.js

驗證輸出。伺服器已啟動

stdout: Child Process 0 executed.

child process exited with code 0
stdout: Child Process 1 executed.

stdout: Child Process 2 executed.

child process exited with code 0
child process exited with code 0

fork() 方法

child_process.fork 方法是 spawn() 的一個特例,用於建立 Node 程序。它的簽名如下:

child_process.fork(modulePath[, args][, options])

引數

以下是所用引數的描述 −

  • modulePath (字串) 子程序中要執行的模組。

  • **args** (陣列) 字串引數列表

  • **options** (物件) 可能包含以下一個或多個選項:

    • **cwd** (字串) 子程序的當前工作目錄。

    • **env** (物件) 環境鍵值對。

    • execPath (字串) 用於建立子程序的可執行檔案。

    • execArgv (陣列) 傳遞給可執行檔案的字串引數列表 (預設值:process.execArgv)。

    • silent (布林值) 如果為 true,則子程序的 stdin、stdout 和 stderr 將被管道傳輸到父程序,否則它們將繼承自父程序,詳情請參閱 spawn() 的 stdio 的“pipe”和“inherit”選項 (預設值為 false)。

    • **uid** (數字) 設定程序的使用者身份。

    • **gid** (數字) 設定程序的組身份。

fork 方法除了擁有普通 ChildProcess 例項中的所有方法外,還返回一個帶有內建通訊通道的物件。

示例

建立兩個名為 support.js 和 master.js 的 js 檔案:

檔案:support.js

console.log("Child Process " + process.argv[2] + " executed." );

檔案:master.js

const fs = require('fs');
const child_process = require('child_process');
 
for(var i=0; i<3; i++) {
   var worker_process = child_process.fork("support.js", [i]);	

   worker_process.on('close', function (code) {
      console.log('child process exited with code ' + code);
   });
}

現在執行 master.js 檢視結果:

$ node master.js

驗證輸出。伺服器已啟動。

Child Process 0 executed.
Child Process 1 executed.
Child Process 2 executed.
child process exited with code 0
child process exited with code 0
child process exited with code 0

Node.js - 打包

JXcore 是一個開源專案,它引入了一個獨特的特性,用於將原始檔和其他資源打包並加密到 JX 包中。

假設您有一個包含許多檔案的大型專案。JXcore 可以將它們全部打包到單個檔案中,以簡化分發。本章將快速概述整個過程,從安裝 JXcore 開始。

JXcore 安裝

安裝 JXcore 非常簡單。這裡我們提供了在您的系統上安裝 JXcore 的分步說明。請按照以下步驟操作:

步驟 1

根據您的作業系統和機器架構,從 https://github.com/jxcore/jxcore 下載 JXcore 包。我們下載了一個在 64 位機器上執行的 Cenots 包。

$ wget https://s3.amazonaws.com/nodejx/jx_rh64.zip

步驟 2

解壓下載的檔案 jx_rh64.zip,並將 jx 二進位制檔案複製到 /usr/bin 或根據您的系統設定複製到任何其他目錄。

$ unzip jx_rh64.zip
$ cp jx_rh64/jx /usr/bin

步驟 3

適當設定 PATH 變數,以便您可以從任何位置執行 jx。

$ export PATH=$PATH:/usr/bin

步驟 4

您可以透過發出如下所示的簡單命令來驗證您的安裝。您應該發現它正在工作並列印其版本號,如下所示:

$ jx --version
v0.10.32

程式碼打包

假設您的專案具有以下目錄,您將所有檔案(包括 Node.js、主檔案 index.js 和所有本地安裝的模組)都儲存在這些目錄中。

drwxr-xr-x  2 root root  4096 Nov 13 12:42 images
-rwxr-xr-x  1 root root 30457 Mar  6 12:19 index.htm
-rwxr-xr-x  1 root root 30452 Mar  1 12:54 index.js
drwxr-xr-x 23 root root  4096 Jan 15 03:48 node_modules
drwxr-xr-x  2 root root  4096 Mar 21 06:10 scripts
drwxr-xr-x  2 root root  4096 Feb 15 11:56 style

要打包上述專案,您只需進入此目錄併發出以下 jx 命令。假設 index.js 是 Node.js 專案的入口檔案:

$ jx package index.js index

您可以使用任何其他包名稱代替 index。我們使用 index 是因為我們希望將主檔名保留為 index.jx。但是,上述命令將打包所有內容,並建立以下兩個檔案:

  • index.jxp 這是一箇中間檔案,包含編譯專案所需完整的專案詳細資訊。

  • index.jx 這是一個二進位制檔案,包含已準備好交付給您的客戶端或生產環境的完整包。

啟動 JX 檔案

假設您的原始 Node.js 專案執行如下:

$ node index.js command_line_arguments

使用 JXcore 編譯您的包後,可以按如下方式啟動它:

$ jx index.jx command_line_arguments

要了解有關 JXcore 的更多資訊,您可以檢視其官方網站。

廣告