一、概述
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是一個(gè)基于Google Chrome的V8引擎的JavaScript運(yùn)行環(huán)境的畴,它允許使用JavaScript語言編寫服務(wù)器端代碼。Node.js具有事件驅(qū)動(dòng)和I/O非阻塞兩大特點(diǎn),輕量而高效。
二、安裝與運(yùn)行
從官網(wǎng)下載最新版本的Node.js蒙幻,直接安裝。安裝時(shí)選擇全部組件衣迷,并添加到環(huán)境變量Path中畏鼓。
運(yùn)行命令:node -v
查看是否安裝成功。
運(yùn)行命令:node
進(jìn)入到node.js的交互環(huán)境壶谒,可以輸入任意JavaScript語句云矫,并查看運(yùn)行結(jié)果。
運(yùn)行命令:node <*.js>
直接執(zhí)行js文件里的內(nèi)容汗菜。
運(yùn)行命令:node --use_strict <*.js>
為js文件開啟嚴(yán)格模式让禀。
安裝Node.js的時(shí)候,會(huì)自動(dòng)安裝包管理工具npm陨界。
運(yùn)行命令:npm -v
查看npm的版本巡揍。
命令行模式會(huì)一次性執(zhí)行js文件,中間沒有交互菌瘪;交互模式則是每一行單獨(dú)執(zhí)行腮敌,可以進(jìn)行交互。
如果不想使用集成的IDE俏扩,可以使用VS Code編輯器進(jìn)行Node.js的開發(fā)和調(diào)試糜工。具體操作和配置可以參照VS Code的官網(wǎng)教程。
Node.js Applications with VS Code
三动猬、模塊
在Node.js環(huán)境中啤斗,一個(gè)js文件就稱之為一個(gè)模塊。使用模塊提高了代碼的可維護(hù)性以及可重用性赁咙,還避免了函數(shù)名和變量名沖突钮莲。
當(dāng)在模塊中定義一個(gè)對象(對象、函數(shù)彼水、數(shù)組等)時(shí)崔拥,可以輸出這個(gè)對象以供其他模塊使用:
module.exports = object_name;
其他模塊要想使用這個(gè)對象,可以在模塊中引入該對象:
var variable_name = require('/path/to/the/module/module_name');
Tips:如果不指定路徑凤覆,Node會(huì)按照內(nèi)置模塊链瓦、全局模塊、當(dāng)前模塊的順序進(jìn)行查找盯桦。
1慈俯、基本模塊
在瀏覽器中,JavaScript有一個(gè)全局對象window拥峦;而在Node.js環(huán)境中贴膘,也有一個(gè)全局對象global。由于JavaScript代碼既能在瀏覽器執(zhí)行略号,也能在Node環(huán)境下執(zhí)行刑峡,可以通過全局對象名稱來判斷JavaScript代碼是在哪個(gè)環(huán)境下執(zhí)行的洋闽。
process也是Node.js提供的一個(gè)對象,表示當(dāng)前Node.js進(jìn)程突梦,進(jìn)程本身的事件由process來處理诫舅。
2、常用內(nèi)置模塊
fs模塊是文件系統(tǒng)模塊宫患,負(fù)責(zé)文件的讀寫操作刊懈,提供了同步和異步兩種方法。大部分作為服務(wù)端的代碼撮奏,必須使用異步代碼俏讹。但是在服務(wù)器啟動(dòng)時(shí)讀取配置文件,或結(jié)束時(shí)寫入狀態(tài)等操作畜吊,因?yàn)檫@些代碼只執(zhí)行一次,所以可以使用同步方法户矢。
讀文本文件:
var fs = require('fs');
fs.readFile('input.txt', 'utf-8', function (err, data) {
if (err) {
console.log(err);
} else {
console.log(data);
}
});
其中玲献,readFile函數(shù)的第一個(gè)參數(shù)是文件名,第二個(gè)參數(shù)是文件編碼梯浪,第三個(gè)參數(shù)是一個(gè)回調(diào)函數(shù)捌年。回調(diào)函數(shù)的第一個(gè)參數(shù)代表錯(cuò)誤信息挂洛,第二個(gè)參數(shù)代表返回結(jié)果礼预。
讀二進(jìn)制文件:
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');
}
});
其中,不需要文件編碼虏劲,并且回調(diào)函數(shù)的data參數(shù)將返回一個(gè)Buffer對象托酸。Buffer對象是一個(gè)包含零個(gè)或任意個(gè)字節(jié)的數(shù)組。
寫文件:
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函數(shù)的第一個(gè)參數(shù)是文件名励堡,第二個(gè)參數(shù)是要寫入的數(shù)據(jù),第三個(gè)參數(shù)是回調(diào)函數(shù)堡掏。
讀取文件的相關(guān)信息:
fs.stat('input.txt', function (err, stat) {
...
});
stream是一個(gè)只能在服務(wù)端使用的模塊应结,用來支持流數(shù)據(jù)結(jié)構(gòu),流中的數(shù)據(jù)是有序的泉唁。流是一個(gè)對象鹅龄,使用時(shí)只需關(guān)注流的事件即可。data事件表示流的數(shù)據(jù)可以讀取亭畜,end事件表示流沒有數(shù)據(jù)可以讀取扮休,error事件表示出現(xiàn)錯(cuò)誤。
讀文件:
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寫入文本數(shù)據(jù)...\n');
ws1.write('END.');
ws1.end();
var ws2 = fs.createWriteStream('output2.txt');
ws2.write(new Buffer('使用Stream寫入二進(jìn)制數(shù)據(jù)...\n', 'utf-8'));
ws2.write(new Buffer('END.', 'utf-8'));
ws2.end();
可以使用管道操作pipe將讀取流和寫入流串聯(lián)起來:
var fs = require('fs');
var rs = fs.createReadStream('input.txt');
var ws = fs.createWriteStream('output.txt');
rs.pipe(ws);
默認(rèn)情況下贱案,當(dāng)讀取流的數(shù)據(jù)讀取完畢肛炮,會(huì)觸發(fā)end事件止吐,并自動(dòng)關(guān)閉寫入流。如果不想關(guān)閉侨糟,需要傳遞額外的參數(shù)碍扔。
readable.pipe(writable, { end: false });
http模塊是web應(yīng)用最常用的一個(gè)模塊,提供了request和response對象秕重。request對象封裝HTTP請求不同,調(diào)用request對象的屬性和方法,可以獲取HTTP請求的所有信息溶耘;response對象封裝HTTP響應(yīng)二拐,調(diào)用response對象的方法,可以把HTTP響應(yīng)返回給瀏覽器凳兵。
var http = require('http');
var server = http.createServer(function (request, response) {
// 獲取HTTP請求的method和url
console.log(request.method + ':' + request.url);
// 將HTTP響應(yīng)的內(nèi)容寫入response
response.writeHead(200, {'Content-Type': 'text/html'});
response.end('<h1>Hello world!</h1>');
});
// 監(jiān)聽8080端口
server.listen(8080);
url模塊可以用來將URL字符串解析成一個(gè)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');
// 解析當(dāng)前目錄
var workDir = path.resolve('.');
// 組合完整的文件路徑
var filePath = path.join(workDir, 'pub', 'index.html');
crypto模塊提供通用的加密和哈希算法饭望。
MD5哈希算法用來給任意數(shù)據(jù)一個(gè)簽名,通常用一個(gè)十六進(jìn)制字符串表示:
const crypto = require('crypto');
const hash = crypto.createHash('md5');
hash.update('Hello, world!');
console.log(hash.digest('hex'));
Hmac哈希算法可以利用MD5等算法形庭,另外還需要一個(gè)密鑰:
const crypto = require('crypto');
const hmac = crypto.createHmac('sha256', 'secret-key');
hmac.update('Hello, world!');
console.log(hmac.digest('hex'));
AES是一種常用的對稱加密算法铅辞,加密與解密使用同一個(gè)密鑰。crypto模塊提供了AES支持萨醒,但需要自行封裝斟珊。DiffieHellman算法是一種密鑰交換協(xié)議,可以讓雙方自行協(xié)商一個(gè)密鑰富纸。crypto模塊還可以處理數(shù)字證書囤踩。
四、Web開發(fā)
使用Node.js開發(fā)Web服務(wù)器端胜嗓,有幾點(diǎn)好處:
- 前后端統(tǒng)一使用JavaScript高职,不需要切換語言
- 異步處理大大提高了運(yùn)行速度
針對Node.js,出現(xiàn)了很多后端相關(guān)的Web框架辞州、ORM框架怔锌、模板引擎、測試框架变过、構(gòu)建工具等埃元。
1、Web框架Express/koa
Express是第一代最流行的Web服務(wù)端框架媚狰,它對Node.js的http進(jìn)行了封裝岛杀。雖然Express的API非常簡單,但是由于是基于ES5崭孤,要實(shí)現(xiàn)異步类嗤,只能使用回調(diào)糊肠。如果異步嵌套層次過多,代碼很難理解遗锣。
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實(shí)現(xiàn)異步。雖然使用generator在寫法上比回調(diào)簡單精偿,但其本意并不是用于異步弧圆。真正設(shè)計(jì)用來實(shí)現(xiàn)異步的是Promise,但Promise的寫法過于復(fù)雜笔咽。
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和關(guān)鍵字async
配合實(shí)現(xiàn)異步,同時(shí)兼容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技術(shù)是將關(guān)系型數(shù)據(jù)庫的表結(jié)構(gòu)映射到對象上,Sequelize返回Promise對象甩十,可以更好地進(jìn)行異步處理勿锅。
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操作數(shù)據(jù)庫,首先需要?jiǎng)?chuàng)建一個(gè)Sequelize實(shí)例枣氧。然后定義數(shù)據(jù)模型Model,使用Sequelize映射數(shù)據(jù)庫表垮刹。這樣就可以調(diào)用實(shí)例的相應(yīng)方法來對數(shù)據(jù)庫進(jìn)行操作达吞。
Sequelize操作數(shù)據(jù)庫的一般步驟:
- 通過Model對象的findAll()方法獲取實(shí)例
- 如果要更新實(shí)例,先對實(shí)例屬性進(jìn)行賦值荒典,然后調(diào)用save()方法
- 如果要?jiǎng)h除實(shí)例酪劫,直接調(diào)用destroy()方法
3、模板引擎Jade/Pug
Node.js默認(rèn)使用Jade作為模板引擎(現(xiàn)改名為Pug)寺董。Jade是Node.js的一個(gè)模塊覆糟,jade文件可以被預(yù)編譯為.js文件,也可以被編譯為目標(biāo)html代碼遮咖。
4滩字、測試框架Mocha
Mocha是JavaScript的單元測試框架,既可以在瀏覽器環(huán)境運(yùn)行御吞,也可以在Node.js的環(huán)境運(yùn)行麦箍。
特點(diǎn):
- 既可以測試簡單的JavaScript函數(shù),又可以測試異步代碼
- 既可以自動(dòng)運(yùn)行所有測試陶珠,也可以只運(yùn)行特定的測試
- 可以支持before挟裂、after、beforeEach和afterEach來編寫初始化代碼
5揍诽、構(gòu)建工具Webpack
6诀蓉、WebSocket協(xié)議
WebSocket利用HTTP協(xié)議建立連接栗竖,在瀏覽器和服務(wù)器之間建立雙向通信的通道,服務(wù)器可以主動(dòng)向?yàn)g覽器發(fā)送消息渠啤,而不需要通過瀏覽器發(fā)送請求狐肢。
首先,WebSocket連接必須由瀏覽器發(fā)起埃篓,因?yàn)檎埱髤f(xié)議是一個(gè)標(biāo)準(zhǔn)的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請求的區(qū)別:
- GET請求的地址以
ws://
開頭 - 請求頭
Upgrade: websocket
和Connection: Upgrade
表示連接將被轉(zhuǎn)換為WebSocket連接 -
Sec-WebSocket-Key
用于標(biāo)識(shí)這個(gè)連接 -
Sec-WebSocket-Version
指定WebSocket的協(xié)議版本
然后,如果服務(wù)器接受請求架专,會(huì)返回響應(yīng):
HTTP/1.1 101 Switching Protocols
Upgrade: websocket
Connection: Upgrade
Sec-WebSocket-Accept: server-random-string
其中同窘,響應(yīng)碼101表示將切換協(xié)議,切換后的協(xié)議通過Upgrade來指定部脚。
在Node.js中想邦,最常用的WebSocket模塊是ws。創(chuàng)建一個(gè)WebSocket的服務(wù)器實(shí)例:
const WebSocket = require('ws');
const WebSocketServer = WebSocket.Server;
const wss = new WebSocketServer({
port: 3000
});
如果有WebSocket請求接入委刘,wss對象可以響應(yīng)connection事件來處理這個(gè)WebSocket丧没。對于每個(gè)WebSocket連接,都要對它綁定某些事件方法來處理不同的事件锡移。
參考文章:
廖雪峰:Node.js教程