目錄
一、認識nodejs
Node.js 是一個基於 Chrome V8 引擎的 JavaScript 運行環境(引自百度百科)。
我們都知道,瀏覽器是客戶端的JavaScript運行環境,相對的,nodejs就是服務端的JavaScript運行環境。
我們說nodejs是服務端的運行環境,並不意味着nodejs只能用於爲web提供後臺服務。nodejs實際上是讓JavaScript可以運行於瀏覽器之外,並且爲JavaScript提供了本地文件系統訪問、數據庫訪問,以及作爲web後臺服務所必需的http等接口,使JavaScript真正成爲一門可以用於web後端的編程語言。JavaScript在nodejs的環境下運行,就像Java在虛擬機環境下運行一樣。
因此,說nodejs是一門編程語言,這顯然是錯誤的。nodejs本身只是爲JavaScript提供了一個可以不在瀏覽器下運行的環境。既然是運行環境,那麼它必定包含能夠解析這門語言的編譯器或解釋器,nodejs的作者選擇了性能卓越的JavaScript引擎:V8(由C++編寫,主要用於Chromium和Chrome)。
僅僅有V8還是不夠的,因爲它只能解析JavaScript本身,無法爲其提供訪問操作系統其他服務的編程接口。要讓JavaScript能作爲服務端語言,至少要爲其提供訪問文件系統、數據庫等的能力,並且提供http服務,nodejs的作者Ryan Dahl以及技術社區爲此使用C++開發了一系列的模塊,如fs(文件系統模塊)、http(http服務模塊)、mysql(mysql數據庫模塊)、oracledb(Oracle模塊)等,這裏的fs和http模塊是核心模塊,內置於nodejs核心運行環境內,無需額外安裝。有了這些模塊,你可以用JavaScript訪問與操作系統相關的更多底層服務。
所以說,客戶端的JavaScript等於ECMAScript + DOM + BOM,而服務端的JavaScript等於ECMAScript + fs + mysql + oracledb + http + os(操作系統模塊) + net(TCP模塊) + …
有了這些模塊的支持,JavaScript纔算完全脫離了瀏覽器環境,具備了與操作系統交互的能力,因此它可以作爲web後端語言使用。
既然在nodejs環境下,JavaScript已經可以與操作系統交互了,那它就不限於作爲web後端語言了。此時的JavaScript還常用於編寫工具,比如常見的webpack、vue-cli、create-react-app等,甚至可以用於編寫桌面應用,如微軟旗下著名的基於electron的開發者工具vscode。
二、nodejs基本概念簡介
1. nodejs與npm
npm是nodejs“附贈”的包管理工具,當然它也是nodejs的作者推薦的包管理工具(nodejs現已交由npm的作者來維護)。當你安裝完nodejs,npm也就安裝完成了。
npm的主要作用是管理第三方包(如一個vue組件、一個react組件,或者打包工具webpack以及各種腳手架等,都稱爲一個包)。使用npm命令,你可以從npm服務器上下載別人上傳的第三方包或命令行到本地使用,也可以上傳自己的本地包供別人使用。
比如你想下載webpack,可以使用以下命令:
npm install webpack -g
install webpack表示要安裝webpack。-g參數表示這個包要安裝到nodejs的根目錄下,這樣可以作爲全局包使用。想要卸載已安裝的包,可以使用uninstall命令。
npm訪問的默認服務器地址是https://registry.npmjs.org/
,上傳下載第三方包的目標服務器都是它。如果使用淘寶鏡像cnpm,訪問的是淘寶的服務器,一般來說,該服務器每10分鐘與官方服務器同步一次。
2. nodejs交互式解釋器(REPL)
不同於Java,JavaScript是一門腳本語言,它的執行不需要經過編譯,因此可以一行一行地執行。基於這個特點,nodejs爲JavaScript提供了一個交互式解釋器,用於在命令行即時編寫和執行JavaScript。
安裝完nodejs後,打開命令行,輸入node
,敲擊回車,即可進入交互式執行環境:
出現一個大於號,表示已經進入交互式環境,接下來你可以輸入JavaScript語句,敲擊回車即可執行:
如果需要連續輸入多行代碼,可以按shift + 回車
,解釋器會換行但暫不執行上一語句。
使用nodejs提供的交互式解釋器,可以方便地編寫並運行少量JavaScript代碼。如果代碼量稍大,可以將代碼保存在一個js文件中,然後用node命令執行該js文件,效果與在交互式環境下連續輸入這些代碼是一致的。
3. nodejs異步編程
nodejs使用了一個事件驅動、非阻塞式 I/O 的模型。它的非阻塞I/O是藉助事件循環實現的。
我們都知道,JavaScript是一門單線程語言。如果同步地執行所有的任務,諸如網絡請求、數據庫查詢這樣的操作將耗費大量的時間,導致程序性能極低。所以和在瀏覽器中一樣,nodejs也採用事件循環機制,通過回調函數來解決這個問題(圖片來自菜鳥教程)。
相比於客戶端JavaScript,服務端JavaScript具有更大的優勢。因爲客戶端瀏覽器類型不一,版本也有所差異,對JavaScript版本的支持性差異很大,給前端頁面開發帶來了很大的限制。但在服務端,nodejs版本由我們決定,所以我們可以更放心地使用高版本語法,比如Generator函數、async函數等,來實現異步編程。
此外,nodejs還提供了child_process
模塊,爲JavaScript提供多進程支持。它比瀏覽器端的webworker
更加強大,因爲webworker
只是一種多線程實現,各個線程之間不具備通信能力,而由child_process
創建的進程是可以通信的。
4. 全局對象global
我們知道,在瀏覽器環境下,全局對象是window。而在nodejs環境下,全局對象是global。
同window一樣,引用global的屬性也可以省略global。global的常見屬性包括:
- __filename:當前腳本的絕對路徑,含文件名
- __dirname:當前腳本所在的路徑,不含文件名
- setTimeout()、clearTimeout()、setInterval()、clearInetrval():用法與瀏覽器環境下一致
- console:控制檯對象,用法與瀏覽器下類似。不過瀏覽器環境下的控制檯由瀏覽器提供,而nodejs環境的控制檯通常指shell終端(即命令行窗口)。
- process:nodejs進程狀態對象。爲process綁定事件監聽,可以在nodejs的各個生命週期下執行操作。與nodejs交互的過程中,我們經常會訪問這個對象。
三、nodejs的幾個常用模塊
nodejs有很多模塊,這裏我們只對其中幾個最爲常用的進行一些簡介,以對nodejs有個大致的瞭解。
1. fs模塊
fs模塊是nodejs提供的文件系統模塊,提供一組類似於UNIX系統的文件操作API。引入fs模塊非常簡單:
let fs = require("fs");
得到了fs模塊後,我們就可以像下面這樣讀取文件系統的一個文件:
// 異步讀取
fs.readFile('input.txt', function (err, data) {
if (err) {
return console.error(err);
}
console.log("異步讀取: " + data.toString());
});
還可以以同步的方式來讀取文件:
// 同步讀取
var data = fs.readFileSync('input.txt');
console.log("同步讀取: " + data.toString());
實際上在Chrome瀏覽器中,JavaScript也具備讀取本地文件內容的能力,但是向文件系統中寫入內容是嚴格禁止的,因爲這會給客戶端安全帶來極大的安全隱患。而在nodejs環境下,由於是服務端,不存在這類安全問題,因此nodejs提供的fs模塊允許向文件系統寫入內容:
var fs = require("fs");
fs.writeFile('input.txt', '新內容', function(err) {
if (err) {
return console.error(err);
}
console.log("數據寫入成功!");
});
這會向當前文件夾下的input.txt寫入“新內容”這幾個字符,如果該文件不存在,將先創建它再寫入。
nodejs提供的文件系統操作接口使得它可以爲各種腳手架、打包工具等提供支持。比如vue或者react的腳手架,實際上就是利用nodejs提供的fs模塊,根據輸入的參數,複製事先寫好的模板文件到目標文件夾下。而打包工具,如webpack,則是對項目文件夾下的文件進行整合壓縮和輸出,也離不開nodejs提供的文件系統操作API。
2. web模塊
web模塊由若干子模塊組成,如http模塊、https模塊、ws模塊、net模塊(提供TCP連接)、url模塊等,爲web服務提供支持。有了這些模塊的支持,nodejs才能爲JavaScript提供完整的服務端環境。此外,在nodejs環境下,JavaScript還可以實現一個簡單的客戶端。
我們以一個簡單的例子來介紹web模塊(例子來自菜鳥教程):
首先我們在當前文件夾下新增一個index.html作爲服務端資源,它的內容很簡單:
index.html
<!DOCTYPE html>
<html>
<body>
Hello World!
</body>
</html>
然後在同級目錄下新建server.js:
server.js
var http = require('http'); // http模塊
var fs = require('fs'); // 文件系統模塊
var url = require('url'); // url解析模塊
// 創建服務器
http.createServer( function (request, response) {
// 解析請求,包括文件名
var pathname = url.parse(request.url).pathname;
// 輸出請求的文件名
console.log("Request for " + pathname + " received.");
// 從文件系統中讀取請求的文件內容
fs.readFile(pathname.substr(1), function (err, data) {
if (err) {
console.log(err);
// HTTP 狀態碼: 404 : NOT FOUND
// Content Type: text/html
response.writeHead(404, {'Content-Type': 'text/html'});
}else{
// HTTP 狀態碼: 200 : OK
// Content Type: text/html
response.writeHead(200, {'Content-Type': 'text/html'});
// 響應文件內容
response.write(data.toString());
}
// 發送響應數據
response.end();
});
}).listen(8080);
console.log('Server running at http://127.0.0.1:8080/');
然後創建一個客戶端文件client.js:
client.js
var http = require('http');
// 用於請求的選項
var options = {
host: 'localhost',
port: '8080',
path: '/index.html'
};
// 處理響應的回調函數
var callback = function(response){
// 不斷更新數據
var body = '';
response.on('data', function(data) {
body += data;
});
response.on('end', function() {
// 數據接收完成
console.log(body);
});
}
// 向服務端發送請求
var req = http.request(options, callback);
req.end();
打開命令行,輸入以下命令:
這樣我們的服務就運行起來了,運行網址是http://127.0.0.1:8080
(如果你在瀏覽器地址欄輸入http://127.0.0.1:8080/index.html
,就可以看到本地的index.html頁面)。
再打開一個命令行,然後輸入:
可以看到,即使沒有瀏覽器,我們也可以通過nodejs提供的http模塊,從http://127.0.0.1:8080
這個網址下請求到index.html的內容。只是此時的html文件由於沒有瀏覽器的解析,只是作爲一串普通字符串返回的。基於這一點,你可以在nodejs環境下,用JavaScript寫一個爬蟲程序,主要過程就是不斷請求網頁,然後對取到的網頁字符串進行分析。
不同於Java,nodejs自行封裝了http服務,因此JavaScript運行在nodejs環境下時不需要其他的web容器(如Tomcat、Jetty等)。
3. 數據庫模塊
作爲服務端環境,訪問數據庫是必不可少的。nodejs爲各大數據庫提供對應的模塊,以使JavaScript可以訪問這些數據庫。常見的模塊如MySQL數據庫的mysql模塊,Oracle數據庫的oracledb模塊、MongoDB的mongodb模塊等。這些模塊不是nodejs的核心模塊,需要從npm下載安裝。
下面我們以Oracle數據庫連接來介紹nodejs的oracledb模塊的簡單用法(需要先安裝oracledb:npm install oracledb):
var oracledb = require('oracledb');
const param = {
user: xxx,
password: xxx,
connectString: xxx(數據庫地址及實例)
};
const sql = "select * from USER where id=:1";
let userId = "123"; // 待查詢的用戶id
oracledb.getConnection(param, function(err, connection){
if (err) {
console.error(err.message);
return;
}
console.log("數據庫連接成功");
// 數組的userId元素將替換sql語句中的:1
connection.execute(sql, [userId], function(err, res){
if (err) {
console.error(err.message);
}
console.log(JSON.stringify(res.rows[0]);
})
})
使用不同的數據庫時,安裝對應的模塊即可。
總結
nodejs是JavaScript的服務端環境,它使得JavaScript不僅可以用於編寫web後端,還可以用於編寫各種工具、腳手架等。
nodejs用很多框架,如express、koa、eggjs等,在實際使用nodejs搭建web服務時,常常直接使用這些框架。nodejs的作者推薦使用中間件模式來開發web後臺服務,每個中間件就是一個處理函數,對請求進行一步處理,類似於Java的過濾器。這樣開發者就可以專注於業務邏輯的開發。
nodejs社區非常活躍,有大量可用的模塊,框架也是數不勝數,這裏只是nodejs初識篇,等以後在工作中深入使用後再進行更細緻的瞭解。