---
tags: Node.js
---
# Node.js
## Node.js 是什麼
Node.js 不是一門語言,也不是框架或庫,他僅僅只是一個 JS 運行時環境(即他像瀏覽器一樣 可以解析、執行 JS 代碼)。
- 瀏覽器中的 JS 是 EcmaScript 與 BOM、DOM
- Node.js 中的 JS 則沒有 BOM 和 DOM,只有 EcmaScript (因為服務端不需處理 BOM 和 DOM)
- 但 Node.js 中的 JS 提供了很多有關服務器操作級別的 API,如:文件讀寫、網路請求、創建 HTTP 服務器等
Node.js 是基於 Google Chrome V8 引擎獨立開發的,該引擎是目前最高效能的 JS 引擎。
另外 NPM 是基於 Node.js 開發出來的包管理工具,也是世界上最大的開源庫生態系統
## Node.js 有兩個特性:
1. 事件驅動
2. 非阻塞 I/O 模型(異步)
## 學習 Node.js 可以學到什麼
1. B/S 編程方式
2. 模塊化編程(require 方法)
3. 異步編程(EX: Promise callback)
4. Node.js API
5. Express Web 框架
## 安裝
先打開終端機 輸入命令 `node -v` 確認電腦是否已有安裝 Node.js
如果沒有出現版本號 則表示當前電腦沒有安裝 這時候就請到官網下載 [Node.js](https://nodejs.org/zh-tw/)
若要更新 最簡單、最快的方式也是直接到 [Node.js 官網](https://nodejs.org/zh-tw/)點擊安裝,就會直接覆蓋升級
- LTS : 長期支持版(穩定維護中)
- Current : 最新版
安裝後再次打開終端機 輸入命令 `node -v` 即可確認電腦是否安裝完成
## 基礎使用方式
創建一個資料夾 `demo` 然後在資料夾中新增 `hello.js` 文件
文件內容為 `console.log('Hello')`
打開終端機到 `demo` 目錄 直接輸入 `node hello.js`
它就會回傳 `Hello` 了
- 注意事項:要執行的 JS 文件嚴禁取名為 `node.js` 最好也不要取中文名。
- 只要是 `.js` 檔案就可以使用命令 `node xxx.js` 來執行
## fs 核心模塊
在瀏覽器中沒有操作文件的方法 但在 Node.js 中可以做到,通過引入核心模塊 `fs` 即可實現。
創建一個 `read.js` 文件 輸入以下代碼:
```javascript=
// 引入文件核心模塊
const fs = require("fs");
// 使用 fs.readFile 方法讀取文件
// 參數1 是要讀取的文件路徑
// 參數2 是回調函數 回調中第一個參數是錯誤對象 第二個是文件內容
fs.readFile("./README.md", (err, data) => {
// 由於文件默認存儲二進制數據
// 然後 Node.js 本身又會把它轉成十六進制 所以輸出結果是十六進制
// 解決看不懂十六進制的問題:使用 toString 方法把 data 轉回正常文字
console.log(data.toString());
});
```
最後開啟終端機 輸入 `node read.js` 即可得到 `README.md` 的文件內容
## http 核心模塊
使用 Node.js 的核心模塊 http 來構建一個 Web 服務器。
創建一個 `http.js` 文件 輸入以下代碼:
```javascript=
// 引入 http 核心模塊
const http = require("http");
// http.createServer 方法創建服務器
// 它會返回一個服務器 所以要用一個變量 server 接收
const server = http.createServer();
// 使用 server.on 方法監聽 reqquest 事件
// 收到 request 請求的時候執行回調 log 收到請求
server.on("request", () => {
console.log("收到請求。");
});
// 最後使用 listen 創建端口號
server.listen(5000, () => {
console.log("running...");
});
```
開啟終端機執行 `node http.js`
出現 `running...` 後 開啟瀏覽器 進入網址 `127.0.0.1:5000/` 即可發送請求。
此時回到終端機就會收到 log 的收到請求。
### 構建一個 Web 服務器的進階版
在發送請求後獲取請求的 url 路徑 接著響應一些文字 代碼如下:
```javascript=
const http = require("http");
const server = http.createServer();
// 主要修改代碼 回調函數中傳入兩參數
// 一個是請求一個是響應
server.on("request", (req, res) => {
// req.url 可以獲得 :5000 後的 url 地址
console.log(req.url);
// res.write 方法可寫入文字 且會被瀏覽器編譯 所以可以寫入標籤
res.write("<h1>TEST RES</h1>");
// res.end 方法是結束響應 不然會一直轉圈圈
res.end()
});
server.listen(5000, () => {
console.log("running...");
});
```
- 注意:在瀏覽器中沒有設置編碼的情況下 默認是使用當前操作系統的編碼
- 中文操作系統的編碼是 gbk 所以假設你 res.write 傳入中文 瀏覽器渲染結果會是中文亂碼
上述問題處理方式:手動設置 `content-type`
設置方法代碼如下:
```javascript=
const http = require("http");
const server = http.createServer();
server.on("request", (req, res) => {
console.log(req.url);
// 主要添加這一行
res.setHeader("Content-Type", "text/plain; charset=utf-8");
// 接著傳入中文測試,即可發現不再是亂碼
res.write("我是中文字!");
res.end()
});
server.listen(5000, () => {
console.log("running...");
});
```
- response.setHeader() 方法只能寫入單個響應頭設定
- response.writeHead() 方法可用物件方式傳入多個響應頭設定,包括狀態碼,內容和多個標頭等
>附上 [content-type 種類連結參考網址](https://www.runoob.com/http/http-content-type.html )
### 發送不同格式的響應
上方提到的都是使用 res.write() 方法響應文字內容
接著說明針對不同響應格式的操作方式:
#### 響應 HTML 格式
實例如下:
```javascript=
var http = require("http");
var fs = require("fs");
http
.createServer(function (req, res) {
// 設置響應頭
res.writeHead(200, { "Content-Type": "text/html" });
// 使用核心模塊 fs 讀取任意的 .html 檔案並將讀取到的內容發送
fs.readFile("./index.html", (err, data) => {
if (err) {
return res.end("Something wrong!");
}
res.end(data);
});
})
.listen(5000, () => {
console.log("Server running...");
});
```
可通過在終端機中輸入 `node 代碼文件名稱` 指令執行
並開啟本地端 輸入端口號 5000 測試實際顯示內容
#### 響應 JSON 格式
實例如下:
```javascript=
var http = require("http");
var fs = require("fs");
http
.createServer(function (req, res) {
// 設置響應頭
res.writeHead(200, { "Content-Type": "application/json" });
// 手寫一段 json 做測試用
let json_response = {
status: 200,
message: "succssful",
result: ["sunday", "monday", "tuesday", "wednesday"],
code: 2000,
};
// 把 json 轉為 string 並發送響應
res.end(JSON.stringify(json_response));
})
.listen(5000, () => {
console.log("Server running...");
});
```
可通過在終端機中輸入 `node 代碼文件名稱` 指令執行
並開啟本地端 輸入端口號 5000 測試實際顯示內容
#### 響應 PDF 格式
實例如下:
```javascript=
var http = require("http");
var fs = require("fs");
http
.createServer(function (req, res) {
// 設置響應頭
res.writeHead(200, { "Content-Type": "application/pdf" });
// 使用核心模塊 fs 讀取 .pdf 並將讀取到的內容發送
fs.readFile("test.pdf", (error, data) => {
if (error) {
return res.end("Something wrong!");
} else {
res.write(data);
res.end();
}
});
})
.listen(5000, () => {
console.log("Server running...");
});
```
可通過在終端機中輸入 `node 代碼文件名稱` 指令執行
並開啟本地端 輸入端口號 5000 測試實際顯示內容
#### 響應 MP3/MP4 格式
實例如下:
```javascript=
var http = require("http");
var fs = require("fs");
http
.createServer(function (req, res) {
// 設置響應頭 mp4 設為 video/mp4 其餘都相同
res.writeHead(200, { "Content-Type": "audio/mp3" });
// exists 用於判斷文件是否存在
fs.exists(
"https://www.learningcontainer.com/wp-content/uploads/2020/02/Kalimba.mp3",
function (exists) {
if (exists) {
var rstream = fs.createReadStream(
"https://www.learningcontainer.com/wp-content/uploads/2020/02/Kalimba.mp3"
);
rstream.pipe(res);
} else {
res.end("Something wrong!");
}
}
);
})
.listen(5000, () => {
console.log("Server running...");
});
```
- createReadStream 用於把東西轉成數據流
- 可以通過 數據流.pipe(res) 方法 把數據流傳給響應
- pipe 是 Node.js 的方法 該方法可理解為是用於連接兩個東西的管道
可通過在終端機中輸入 `node 代碼文件名稱` 指令執行
並開啟本地端 輸入端口號 5000 測試實際顯示內容
## require 是什麼
首先你要知道在 Node.js 中無法一次執行多個 js 腳本文件
你沒辦法通過命令 `node a.js b.js` 來同時執行 a.js 和 b.js 文件,它只會執行第一個(即 a.js 文件)
那若要做到同時執行 a.js 和 b.js 文件,就需要使用這個神奇的 require 來處理了
舉例來說,我們就可以在 a.js 中 require 那個也需要執行的 b.js 文件
代碼如下:
```javascript=
// a.js 文件內容如下
console.log('a start')
require('./b.js') // 這行就是引入 b.js 用的
console.log('a end')
// b.js 文件內容如下
console.log('bbb')
```
然後在終端機中執行命令 `node a.js` 輸出結果就是:
1. a start
2. bbb
3. a end
另外要注意的是 Node.js 中沒有全局作用域 只有模塊作用域(可理解為文件作用域)
即在 a.js 中定義的變量或函數 即使在 a.js 引入 b.js 也無法在 b.js 中調用到 a.js 所定義的東西
那同理, a.js 也無法使用到 b.js 定義的變量或函數,他們的作用域都只在自己的文件本身,這就是模塊作用域,a.js 跟 b.js 各自是一個模塊。
- 補充:`require` 可以省略後綴名(即省略`.js`) 因為它默認就會找 js 檔案
- `require` 的查找機制為: `.js`檔 > `.json`檔 > `.node`檔
### exports 是什麼
首先要知道,在每個模塊中默認都有一個 `exports` 對象
每個模塊都可以通過 `exports` 對象來導出模塊成員
舉例代碼如下:
```javascript=
// b.js 文件中的內容
// 我們直接用 exports.xxx 來導出這個 function
exports.add = function(x,y){
return x + y
}
// a.js 文件中的內容
const b = require('./b.js') // 引入 b.js 文件為 b
console.log(b.add(10,20)) // 通過 b.add 就可以調用到 b.js 文件中的該函式了
// 這個 add 就是在 b.js 中通過 exports 導出的名稱 add
// 所以此時若執行 `node a.js` 就可以獲得 30
```
- 默認 exports 是一個空對象,每個文件模塊中都有這個對象存在,單看你是否要使用。
## 模板引擎的使用
首先介紹下 `art-template` 模板引擎,他在瀏覽器與後端皆可使用。
使用方法為:
1. 安裝 開啟終端機輸入 `npm i art-template`
2. 引入 `const template = require('art-template')`
3. 撰寫
- 撰寫方法代碼如下:
```javascript=
const templaet = require('art-template')
template.render('<h1>{{title}}</h1>',{ title : '我是標題' })
```
附上 [art-template 官網連結](https://aui.github.io/art-template/zh-cn/)參考。
.......待續