Node.js快速入門

一、概述

Node.js? is a JavaScript runtime built on Chrome's V8 JavaScript engine. Node.js uses an event-driven, non-blocking I/O model that makes it lightweight and efficient. Node.js' package ecosystem, npm, is the largest ecosystem of open source libraries in the world.

Node.js是一個基于Google Chrome的V8引擎的JavaScript運行環境,它允許使用JavaScript語言編寫服務器端代碼。Node.js具有事件驅動和I/O非阻塞兩大特點,輕量而高效。

官網地址

二、安裝與運行

從官網下載最新版本的Node.js,直接安裝。安裝時選擇全部組件,并添加到環境變量Path中。

運行命令:node -v查看是否安裝成功。

運行命令:node進入到node.js的交互環境,可以輸入任意JavaScript語句,并查看運行結果。

運行命令:node <*.js>直接執行js文件里的內容。

運行命令:node --use_strict <*.js>為js文件開啟嚴格模式。

安裝Node.js的時候,會自動安裝包管理工具npm。

運行命令:npm -v查看npm的版本。

命令行模式會一次性執行js文件,中間沒有交互;交互模式則是每一行單獨執行,可以進行交互。

如果不想使用集成的IDE,可以使用VS Code編輯器進行Node.js的開發和調試。具體操作和配置可以參照VS Code的官網教程。

VS Code官網地址

Node.js Applications with VS Code

三、模塊

在Node.js環境中,一個js文件就稱之為一個模塊。使用模塊提高了代碼的可維護性以及可重用性,還避免了函數名和變量名沖突。

當在模塊中定義一個對象(對象、函數、數組等)時,可以輸出這個對象以供其他模塊使用:

module.exports = object_name;

其他模塊要想使用這個對象,可以在模塊中引入該對象:

var variable_name = require('/path/to/the/module/module_name');

Tips:如果不指定路徑,Node會按照內置模塊、全局模塊、當前模塊的順序進行查找。

1、基本模塊

在瀏覽器中,JavaScript有一個全局對象window;而在Node.js環境中,也有一個全局對象global。由于JavaScript代碼既能在瀏覽器執行,也能在Node環境下執行,可以通過全局對象名稱來判斷JavaScript代碼是在哪個環境下執行的。

process也是Node.js提供的一個對象,表示當前Node.js進程,進程本身的事件由process來處理。

2、常用內置模塊

fs模塊是文件系統模塊,負責文件的讀寫操作,提供了同步和異步兩種方法。大部分作為服務端的代碼,必須使用異步代碼。但是在服務器啟動時讀取配置文件,或結束時寫入狀態等操作,因為這些代碼只執行一次,所以可以使用同步方法。

讀文本文件:

var fs = require('fs');
fs.readFile('input.txt', 'utf-8', function (err, data) {
  if (err) {
    console.log(err);
  } else {
    console.log(data);
  }
});

其中,readFile函數的第一個參數是文件名,第二個參數是文件編碼,第三個參數是一個回調函數。回調函數的第一個參數代表錯誤信息,第二個參數代表返回結果。

讀二進制文件:

var fs = require('fs');
fs.readFile('input.png', function (err, data) {
  if (err) {
    console.log(err);
  } else {
    console.log(data);
    console.log(data.length + ' bytes');
  }
});

其中,不需要文件編碼,并且回調函數的data參數將返回一個Buffer對象。Buffer對象是一個包含零個或任意個字節的數組。

寫文件:

var fs = require('fs');
var data = 'Hello world';
fs.writeFile('output.txt', data, function (err) {
  if (err) {
    console.log(err);
  } else {
    console.log('success');
  }
});

其中,writeFile函數的第一個參數是文件名,第二個參數是要寫入的數據,第三個參數是回調函數。

讀取文件的相關信息:

fs.stat('input.txt', function (err, stat) {
  ...
});

stream是一個只能在服務端使用的模塊,用來支持流數據結構,流中的數據是有序的。流是一個對象,使用時只需關注流的事件即可。data事件表示流的數據可以讀取,end事件表示流沒有數據可以讀取,error事件表示出現錯誤。

讀文件:

var fs = require('fs');
var rs = fs.createReadStream('input.txt', 'utf-8');
rs.on('data', function (chunk) {
  console.log('DATA:');
  console.log(chunk);
});
rs.on('end', function () {
  console.log('END');
});
rs.on('error', function (err) {
  console.log('ERROR: ' + err);
});

寫文件:

var fs = require('fs');
var ws1 = fs.createWriteStream('output1.txt', 'utf-8');
ws1.write('使用Stream寫入文本數據...\n');
ws1.write('END.');
ws1.end();

var ws2 = fs.createWriteStream('output2.txt');
ws2.write(new Buffer('使用Stream寫入二進制數據...\n', 'utf-8'));
ws2.write(new Buffer('END.', 'utf-8'));
ws2.end();

可以使用管道操作pipe將讀取流和寫入流串聯起來:

var fs = require('fs');
var rs = fs.createReadStream('input.txt');
var ws = fs.createWriteStream('output.txt');
rs.pipe(ws);

默認情況下,當讀取流的數據讀取完畢,會觸發end事件,并自動關閉寫入流。如果不想關閉,需要傳遞額外的參數。

readable.pipe(writable, { end: false });

http模塊是web應用最常用的一個模塊,提供了request和response對象。request對象封裝HTTP請求,調用request對象的屬性和方法,可以獲取HTTP請求的所有信息;response對象封裝HTTP響應,調用response對象的方法,可以把HTTP響應返回給瀏覽器。

var http = require('http');
var server = http.createServer(function (request, response) {
  // 獲取HTTP請求的method和url
  console.log(request.method + ':' + request.url);
  // 將HTTP響應的內容寫入response
  response.writeHead(200, {'Content-Type': 'text/html'});
  response.end('<h1>Hello world!</h1>');
});
// 監聽8080端口
server.listen(8080);

url模塊可以用來將URL字符串解析成一個Url對象,從而獲取想要的信息。

var url = require('url');
console.log(url.parse('http://user:pass@host.com:8080/path/to/file?query=string#hash'));

path模塊可以用來處理本地文件目錄。

var path = require('path');
// 解析當前目錄
var workDir = path.resolve('.');
// 組合完整的文件路徑
var filePath = path.join(workDir, 'pub', 'index.html');

crypto模塊提供通用的加密和哈希算法。

MD5哈希算法用來給任意數據一個簽名,通常用一個十六進制字符串表示:

const crypto = require('crypto');
const hash = crypto.createHash('md5');
hash.update('Hello, world!');
console.log(hash.digest('hex'));

Hmac哈希算法可以利用MD5等算法,另外還需要一個密鑰:

const crypto = require('crypto');
const hmac = crypto.createHmac('sha256', 'secret-key');
hmac.update('Hello, world!');
console.log(hmac.digest('hex'));

AES是一種常用的對稱加密算法,加密與解密使用同一個密鑰。crypto模塊提供了AES支持,但需要自行封裝。DiffieHellman算法是一種密鑰交換協議,可以讓雙方自行協商一個密鑰。crypto模塊還可以處理數字證書。

四、Web開發

使用Node.js開發Web服務器端,有幾點好處:

  • 前后端統一使用JavaScript,不需要切換語言
  • 異步處理大大提高了運行速度

針對Node.js,出現了很多后端相關的Web框架、ORM框架、模板引擎、測試框架、構建工具等。

1、Web框架Express/koa

Express是第一代最流行的Web服務端框架,它對Node.js的http進行了封裝。雖然Express的API非常簡單,但是由于是基于ES5,要實現異步,只能使用回調。如果異步嵌套層次過多,代碼很難理解。

var express = require('express');
var app = express();
app.get('/', function (req, res) {
  res.send('Hello World!');
});
app.listen(3000, function () {
  console.log('Example app listening on port 3000!');
});

koa1基于ES6,使用generator實現異步。雖然使用generator在寫法上比回調簡單,但其本意并不是用于異步。真正設計用來實現異步的是Promise,但Promise的寫法過于復雜。

var koa = require('koa');
var app = koa();
app.use('/test', function *() {
  yield doReadFile1();
  var data = yield doReadFile2();
  this.body = data;
});
app.listen(3000);

koa2基于ES7,使用Promise和關鍵字async配合實現異步,同時兼容generator的寫法。

app.use(async (ctx, next) => {
  await next();
  var data = await doReadFile();
  ctx.response.type = 'text/plain';
  ctx.response.body = data;
});

2、ORM框架Sequelize

ORM技術是將關系型數據庫的表結構映射到對象上,Sequelize返回Promise對象,可以更好地進行異步處理。

ES6:

Pet.findAll()
 .then(function (pets) {
   for (let pet in pets) {
     console.log(`${pet.id}: ${pet.name}`);
   }
 }).catch(function (err) {
   // error
 });

ES7:

(async () => {
  var pets = await Pet.findAll();
})();

要想使用Sequelize操作數據庫,首先需要創建一個Sequelize實例。然后定義數據模型Model,使用Sequelize映射數據庫表。這樣就可以調用實例的相應方法來對數據庫進行操作。

Sequelize操作數據庫的一般步驟:

  • 通過Model對象的findAll()方法獲取實例
  • 如果要更新實例,先對實例屬性進行賦值,然后調用save()方法
  • 如果要刪除實例,直接調用destroy()方法

3、模板引擎Jade/Pug

Node.js默認使用Jade作為模板引擎(現改名為Pug)。Jade是Node.js的一個模塊,jade文件可以被預編譯為.js文件,也可以被編譯為目標html代碼。

主頁

4、測試框架Mocha

Mocha是JavaScript的單元測試框架,既可以在瀏覽器環境運行,也可以在Node.js的環境運行。

特點:

  • 既可以測試簡單的JavaScript函數,又可以測試異步代碼
  • 既可以自動運行所有測試,也可以只運行特定的測試
  • 可以支持before、after、beforeEach和afterEach來編寫初始化代碼

5、構建工具Webpack

6、WebSocket協議

WebSocket利用HTTP協議建立連接,在瀏覽器和服務器之間建立雙向通信的通道,服務器可以主動向瀏覽器發送消息,而不需要通過瀏覽器發送請求。

首先,WebSocket連接必須由瀏覽器發起,因為請求協議是一個標準的HTTP請求。

GET ws://localhost:3000/ws/chat HTTP/1.1
Host: localhost
Upgrade: websocket
Connection: Upgrade
Origin: http://localhost:3000
Sec-WebSocket-Key: client-random-string
Sec-WebSocket-Version: 13

與普通HTTP請求的區別:

  • GET請求的地址以ws://開頭
  • 請求頭Upgrade: websocketConnection: Upgrade表示連接將被轉換為WebSocket連接
  • Sec-WebSocket-Key用于標識這個連接
  • Sec-WebSocket-Version指定WebSocket的協議版本

然后,如果服務器接受請求,會返回響應:

HTTP/1.1 101 Switching Protocols
Upgrade: websocket
Connection: Upgrade
Sec-WebSocket-Accept: server-random-string

其中,響應碼101表示將切換協議,切換后的協議通過Upgrade來指定。

在Node.js中,最常用的WebSocket模塊是ws。創建一個WebSocket的服務器實例:

const WebSocket = require('ws');
const WebSocketServer = WebSocket.Server;
const wss = new WebSocketServer({
  port: 3000
});

如果有WebSocket請求接入,wss對象可以響應connection事件來處理這個WebSocket。對于每個WebSocket連接,都要對它綁定某些事件方法來處理不同的事件。


參考文章:

廖雪峰:Node.js教程

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。

推薦閱讀更多精彩內容

  • 1. 開始使用nodejs 1.1. Hello Word 好了,讓我們開始實現第一個 Node.js 程序吧。打...
    angelwgh閱讀 2,356評論 1 8
  • Node.js是目前非?;馃岬募夹g,但是它的誕生經歷卻很奇特。 眾所周知,在Netscape設計出JavaScri...
    Myselfyan閱讀 4,101評論 2 58
  • Node.js是目前非常火熱的技術,但是它的誕生經歷卻很奇特。 眾所周知,在Netscape設計出JavaScri...
    w_zhuan閱讀 3,641評論 2 41
  • 古風版的【愛請轉移】 若有幸他也聽聞 我依然在等
    397f2d7b7d0b閱讀 261評論 1 0
  • 大連北站開往丹東站,途徑莊河北站。 金州站,廣寧寺站,登沙河站,皮口站····· 我的天每一站都停! 如果是個電影...
    郭永偉閱讀 225評論 0 0