個人主頁:Guiat
歸屬專欄:node.js
文章目錄
- 1. Node.js簡介
- 1.1 Node.js的核心特點
- 1.2 Node.js適用場景
- 2. 第一個Node.js程序
- 2.1 創建并運行Hello World
- 2.2 創建簡單的HTTP服務器
- 3. Node.js核心概念
- 3.1 模塊系統
- 3.1.1 創建和導出模塊
- 3.1.2 導入和使用模塊
- 3.1.3 ES模塊語法(ES Modules)
- 3.2 事件循環
- 3.2.1 事件循環示例
- 3.2.2 異步文件操作示例
- 3.3 事件發射器 (EventEmitter)
- 3.3.1 自定義事件發射器
- 3.4 流 (Streams)
- 3.4.1 流的類型
- 3.4.2 文件流示例
- 3.4.3 使用pipe()簡化流操作
- 3.4.4 創建自定義流
- 4. 文件系統操作
- 4.1 同步與異步文件操作
- 4.2 寫入文件
- 4.3 目錄操作
- 4.4 文件路徑操作
- 5. HTTP服務器開發
- 5.1 創建基本HTTP服務器
- 5.2 處理HTTP請求和路由
- 5.3 處理靜態文件
正文
1. Node.js簡介
Node.js是一個開源、跨平臺的JavaScript運行環境,它允許開發者使用JavaScript來編寫服務器端代碼。Node.js采用Google Chrome的V8引擎來執行JavaScript代碼,具有非阻塞I/O和事件驅動機制,使其成為構建高性能網絡應用的理想選擇。
1.1 Node.js的核心特點
- 非阻塞I/O:執行I/O操作時不會阻塞線程
- 事件驅動:基于事件循環處理并發操作
- 單線程:使用單線程處理多個并發連接
- 跨平臺:可在Windows、macOS和Linux等多種操作系統上運行
- npm:擁有世界上最大的開源代碼庫生態系統
1.2 Node.js適用場景
Node.js特別適合以下場景:
- 實時應用(聊天、游戲服務器)
- REST API和微服務
- 單頁面應用(SPA)的后端
- 流處理和數據密集型應用
- 命令行工具
2. 第一個Node.js程序
2.1 創建并運行Hello World
創建一個名為hello.js
的文件,內容如下:
// 第一個Node.js程序
console.log("Hello, Node.js!");
在命令行中運行:
node hello.js
輸出結果:
Hello, Node.js!
2.2 創建簡單的HTTP服務器
創建一個基本的Web服務器:
// 創建簡單的HTTP服務器
const http = require('http');// 創建HTTP服務器
const server = http.createServer((req, res) => {// 設置響應頭res.writeHead(200, {'Content-Type': 'text/plain'});// 發送響應數據res.end('Hello World from Node.js Server!');
});// 服務器監聽3000端口
const PORT = 3000;
server.listen(PORT, () => {console.log(`Server running at http://localhost:${PORT}/`);
});
運行服務器:
node server.js
現在可以在瀏覽器中訪問http://localhost:3000
查看結果。
3. Node.js核心概念
3.1 模塊系統
Node.js使用CommonJS模塊系統,允許將代碼分割成可重用的部分。
3.1.1 創建和導出模塊
// math.js - 創建一個數學工具模塊
function add(a, b) {return a + b;
}function subtract(a, b) {return a - b;
}function multiply(a, b) {return a * b;
}function divide(a, b) {if (b === 0) {throw new Error('Cannot divide by zero');}return a / b;
}// 導出多個函數
module.exports = {add,subtract,multiply,divide
};
3.1.2 導入和使用模塊
// app.js - 使用數學工具模塊
const math = require('./math');console.log(`2 + 3 = ${math.add(2, 3)}`);
console.log(`5 - 2 = ${math.subtract(5, 2)}`);
console.log(`4 * 6 = ${math.multiply(4, 6)}`);
console.log(`10 / 2 = ${math.divide(10, 2)}`);// 也可以使用解構賦值
const { add, multiply } = require('./math');
console.log(`4 + 5 = ${add(4, 5)}`);
console.log(`3 * 7 = ${multiply(3, 7)}`);
3.1.3 ES模塊語法(ES Modules)
Node.js也支持ES模塊語法,需要將文件后綴改為.mjs
或在package.json
中設置"type": "module"
。
// mathES.mjs - ES模塊語法
export function add(a, b) {return a + b;
}export function subtract(a, b) {return a - b;
}// 導出默認值
export default {name: 'Math Utils',version: '1.0.0'
};
// appES.mjs - 導入ES模塊
import { add, subtract } from './mathES.mjs';
import mathInfo from './mathES.mjs';console.log(`ES模塊: 2 + 3 = ${add(2, 3)}`);
console.log(`模塊信息: ${mathInfo.name} v${mathInfo.version}`);
3.2 事件循環
Node.js的事件循環是其非阻塞I/O模型的核心,它允許Node.js執行非阻塞操作。
3.2.1 事件循環示例
console.log('1. 開始執行');// 延遲執行 (宏任務)
setTimeout(() => {console.log('4. setTimeout 回調執行');
}, 0);// Promise (微任務)
Promise.resolve().then(() => {console.log('3. Promise 回調執行');
});console.log('2. 結束執行');// 輸出順序:
// 1. 開始執行
// 2. 結束執行
// 3. Promise 回調執行
// 4. setTimeout 回調執行
3.2.2 異步文件操作示例
const fs = require('fs');console.log('1. 開始讀取文件');// 異步讀取文件
fs.readFile('example.txt', 'utf8', (err, data) => {if (err) {console.error('讀取文件出錯:', err);return;}console.log('3. 文件內容:', data);
});console.log('2. 讀取文件的請求已發出,繼續執行其他操作');
3.3 事件發射器 (EventEmitter)
EventEmitter是Node.js的核心模塊,用于實現事件驅動架構。
const EventEmitter = require('events');// 創建事件發射器實例
class MyEmitter extends EventEmitter {}
const myEmitter = new MyEmitter();// 注冊事件監聽器
myEmitter.on('event', (a, b) => {console.log('事件發生了!', a, b);
});// 注冊只執行一次的事件監聽器
myEmitter.once('onceEvent', () => {console.log('這個事件只會觸發一次');
});// 觸發事件
myEmitter.emit('event', 'a', 'b');
myEmitter.emit('onceEvent');
myEmitter.emit('onceEvent'); // 這次不會觸發監聽器
3.3.1 自定義事件發射器
const EventEmitter = require('events');// 創建一個用戶類,繼承EventEmitter
class User extends EventEmitter {constructor(name) {super();this.name = name;}sayHello() {console.log(`${this.name} says hello!`);// 觸發事件this.emit('hello', this.name);}sayGoodbye() {console.log(`${this.name} says goodbye!`);// 觸發事件this.emit('goodbye', this.name);}
}// 創建用戶實例
const user = new User('John');// 添加事件監聽器
user.on('hello', (name) => {console.log(`Hello event was triggered by ${name}`);
});user.on('goodbye', (name) => {console.log(`Goodbye event was triggered by ${name}`);
});// 調用方法,觸發事件
user.sayHello();
user.sayGoodbye();
3.4 流 (Streams)
流是用于處理讀寫數據的抽象接口,尤其適合處理大文件。
3.4.1 流的類型
- 可讀流 (Readable):用于讀取數據
- 可寫流 (Writable):用于寫入數據
- 雙工流 (Duplex):可讀可寫
- 轉換流 (Transform):在讀寫過程中修改數據
3.4.2 文件流示例
const fs = require('fs');// 創建可讀流
const readStream = fs.createReadStream('source.txt', 'utf8');// 創建可寫流
const writeStream = fs.createWriteStream('destination.txt');// 處理流事件
readStream.on('data', (chunk) => {console.log(`接收到 ${chunk.length} 字節的數據`);// 寫入數據到可寫流writeStream.write(chunk);
});readStream.on('end', () => {writeStream.end(); // 結束寫入流console.log('讀取完成');
});readStream.on('error', (err) => {console.error('讀取錯誤:', err);
});writeStream.on('finish', () => {console.log('寫入完成');
});writeStream.on('error', (err) => {console.error('寫入錯誤:', err);
});
3.4.3 使用pipe()簡化流操作
const fs = require('fs');// 創建可讀流和可寫流
const readStream = fs.createReadStream('source.txt');
const writeStream = fs.createWriteStream('destination.txt');// 使用pipe直接將可讀流連接到可寫流
readStream.pipe(writeStream);// 處理事件
readStream.on('end', () => {console.log('讀取完成');
});writeStream.on('finish', () => {console.log('寫入完成');
});
3.4.4 創建自定義流
const { Transform } = require('stream');// 創建轉換流,將文本轉為大寫
class UppercaseTransform extends Transform {_transform(chunk, encoding, callback) {// 轉換數據const upperChunk = chunk.toString().toUpperCase();// 推送轉換后的數據this.push(upperChunk);// 調用回調,表示處理完成callback();}
}// 使用自定義轉換流
const upperCaseStream = new UppercaseTransform();// 從標準輸入讀取,經過轉換后寫入標準輸出
process.stdin.pipe(upperCaseStream).pipe(process.stdout);
4. 文件系統操作
Node.js提供了fs
模塊用于與文件系統交互。
4.1 同步與異步文件操作
const fs = require('fs');// 同步讀取文件(阻塞)
try {const data = fs.readFileSync('file.txt', 'utf8');console.log('同步讀取文件:', data);
} catch (err) {console.error('同步讀取錯誤:', err);
}// 異步讀取文件(非阻塞)
fs.readFile('file.txt', 'utf8', (err, data) => {if (err) {console.error('異步讀取錯誤:', err);return;}console.log('異步讀取文件:', data);
});// 使用Promise API (Node.js 10+)
fs.promises.readFile('file.txt', 'utf8').then(data => {console.log('Promise讀取文件:', data);}).catch(err => {console.error('Promise讀取錯誤:', err);});// 使用async/await (Node.js 10+)
async function readFileAsync() {try {const data = await fs.promises.readFile('file.txt', 'utf8');console.log('Async/Await讀取文件:', data);} catch (err) {console.error('Async/Await讀取錯誤:', err);}
}readFileAsync();
4.2 寫入文件
const fs = require('fs');// 同步寫入
try {fs.writeFileSync('output1.txt', 'Hello, Node.js! (同步寫入)', 'utf8');console.log('同步寫入完成');
} catch (err) {console.error('同步寫入錯誤:', err);
}// 異步寫入
fs.writeFile('output2.txt', 'Hello, Node.js! (異步寫入)', 'utf8', (err) => {if (err) {console.error('異步寫入錯誤:', err);return;}console.log('異步寫入完成');
});// 追加內容到文件
fs.appendFile('output2.txt', '\n這是追加的內容', 'utf8', (err) => {if (err) {console.error('追加錯誤:', err);return;}console.log('追加完成');
});
4.3 目錄操作
const fs = require('fs');
const path = require('path');// 創建目錄
fs.mkdir('new-directory', (err) => {if (err) {console.error('創建目錄錯誤:', err);return;}console.log('目錄創建成功');
});// 遞歸創建多級目錄
fs.mkdir('parent/child/grandchild', { recursive: true }, (err) => {if (err) {console.error('創建多級目錄錯誤:', err);return;}console.log('多級目錄創建成功');
});// 讀取目錄內容
fs.readdir('.', (err, files) => {if (err) {console.error('讀取目錄錯誤:', err);return;}console.log('當前目錄文件:');files.forEach(file => {console.log(`- ${file}`);});
});// 獲取文件信息
fs.stat('file.txt', (err, stats) => {if (err) {console.error('獲取文件信息錯誤:', err);return;}console.log('文件信息:');console.log(`- 是文件? ${stats.isFile()}`);console.log(`- 是目錄? ${stats.isDirectory()}`);console.log(`- 文件大小: ${stats.size} 字節`);console.log(`- 創建時間: ${stats.birthtime}`);console.log(`- 修改時間: ${stats.mtime}`);
});
4.4 文件路徑操作
const path = require('path');// 路徑拼接 (跨平臺兼容)
const fullPath = path.join(__dirname, 'subfolder', 'file.txt');
console.log('拼接路徑:', fullPath);// 解析路徑
const pathInfo = path.parse('/home/user/documents/file.txt');
console.log('路徑信息:', pathInfo);
/*
輸出:
{root: '/',dir: '/home/user/documents',base: 'file.txt',ext: '.txt',name: 'file'
}
*/// 規范化路徑
console.log('規范化路徑:', path.normalize('/home//user/../user/docs/'));
// 輸出: /home/user/docs/// 獲取絕對路徑
console.log('絕對路徑:', path.resolve('subfolder', 'file.txt'));// 獲取擴展名
console.log('擴展名:', path.extname('file.txt')); // 輸出: .txt
5. HTTP服務器開發
5.1 創建基本HTTP服務器
const http = require('http');// 創建HTTP服務器
const server = http.createServer((req, res) => {// 獲取請求信息const { method, url, headers } = req;console.log(`收到 ${method} 請求: ${url}`);// 根據URL路徑提供不同響應if (url === '/') {// 設置響應頭res.writeHead(200, {'Content-Type': 'text/html'});// 發送響應數據res.end(`<html><head><title>Node.js服務器</title></head><body><h1>歡迎來到Node.js服務器!</h1><p>當前時間: ${new Date().toLocaleString()}</p><ul><li><a href="/about">關于我們</a></li><li><a href="/contact">聯系我們</a></li></ul></body></html>`);} else if (url === '/about') {res.writeHead(200, {'Content-Type': 'text/html'});res.end(`<html><head><title>關于我們</title></head><body><h1>關于我們</h1><p>這是一個簡單的Node.js HTTP服務器示例。</p><a href="/">返回首頁</a></body></html>`);}else if (url === '/contact') {res.writeHead(200, {'Content-Type': 'text/html'});res.end(`<html><head><title>聯系我們</title></head><body><h1>聯系我們</h1><p>Email: example@example.com</p><a href="/">返回首頁</a></body></html>`);}else if (url === '/api/time') {// 返回JSON數據res.writeHead(200, {'Content-Type': 'application/json'});res.end(JSON.stringify({time: new Date().toISOString(),timestamp: Date.now()}));}else {// 404 Not Foundres.writeHead(404, {'Content-Type': 'text/html'});res.end(`<html><head><title>404 Not Found</title></head><body><h1>404 - 頁面不存在</h1><p>請求的URL "${url}" 不存在</p><a href="/">返回首頁</a></body></html>`);}
});// 服務器監聽端口
const PORT = process.env.PORT || 3000;
server.listen(PORT, () => {console.log(`服務器運行在 http://localhost:${PORT}/`);
});
5.2 處理HTTP請求和路由
const http = require('http');
const url = require('url');// 路由處理函數
const routes = {'GET': {'/': (req, res) => {res.writeHead(200, {'Content-Type': 'text/html'});res.end('<h1>首頁</h1><p>歡迎訪問我們的網站</p>');},'/about': (req, res) => {res.writeHead(200, {'Content-Type': 'text/html'});res.end('<h1>關于我們</h1><p>這是關于頁面</p>');},'/api/users': (req, res) => {const users = [{ id: 1, name: 'Alice' },{ id: 2, name: 'Bob' },{ id: 3, name: 'Charlie' }];res.writeHead(200, {'Content-Type': 'application/json'});res.end(JSON.stringify(users));}},'POST': {'/api/users': (req, res) => {let body = '';// 收集請求體數據req.on('data', chunk => {body += chunk.toString();});// 請求體接收完畢req.on('end', () => {try {const userData = JSON.parse(body);// 處理用戶數據...res.writeHead(201, {'Content-Type': 'application/json'});res.end(JSON.stringify({message: '用戶創建成功',user: userData}));} catch (error) {res.writeHead(400, {'Content-Type': 'application/json'});res.end(JSON.stringify({error: '無效的JSON數據'}));}});}}
};// 創建HTTP服務器
const server = http.createServer((req, res) => {// 解析URL和查詢參數const parsedUrl = url.parse(req.url, true);const path = parsedUrl.pathname;const method = req.method.toUpperCase();const query = parsedUrl.query;console.log(`${method} ${path}`);// 查找路由處理函數const routeHandler = routes[method] && routes[method][path];if (routeHandler) {// 將查詢參數附加到請求對象req.query = query;// 調用路由處理函數routeHandler(req, res);} else {// 未找到路由處理函數,返回404res.writeHead(404, {'Content-Type': 'text/html'});res.end('<h1>404 - 頁面不存在</h1>');}
});// 啟動服務器
const PORT = 3000;
server.listen(PORT, () => {console.log(`服務器運行在 http://localhost:${PORT}/`);
});
5.3 處理靜態文件
const http = require('http');
const fs = require('fs');
const path = require('path');// 靜態文件目錄
const PUBLIC_DIR = path.join(__dirname, 'public');// MIME類型映射
const MIME_TYPES = {'.html': 'text/html','.css': 'text/css','.js': 'text/javascript','.json': 'application/json','.png': 'image/png','.jpg': 'image/jpeg','.jpeg': 'image/jpeg','.gif': 'image/gif','.svg': 'image/svg+xml','.ico': 'image/x-icon','.txt': 'text/plain'
};// 創建HTTP服務器
const server = http.createServer((req, res) => {// 僅處理GET請求if (req.method !== 'GET') {res.writeHead(405, {'Content-Type': 'text/plain'});res.end('Method Not Allowed');return;}// 獲取請求路徑let filePath = path.join(PUBLIC_DIR, req.url === '/' ? 'index.html' : req.url);// 檢查文件是否存在fs.stat(filePath, (err, stats) => {if (err) {// 文件不存在,返回404res.writeHead(404, {'Content-Type': 'text/html'});res.end('<h1>404 - 文件未找到</h1>');return;}// 如果是目錄,嘗試加載index.htmlif (stats.isDirectory()) {filePath = path.join(filePath, 'index.html');}// 獲取文件擴展名const extname = path.extname(filePath);// 獲取MIME類型const contentType = MIME_TYPES[extname] || 'application/octet-stream';// 讀取并發送文件fs.readFile(filePath, (err, content) => {if (err) {if (err.code === 'ENOENT') {// 文件不存在res.writeHead(404, {'Content-Type': 'text/html'});res.end('<h1>404 - 文件未找到</h1>');} else {// 服務器錯誤res.writeHead(500, {'Content-Type': 'text/html'});res.end('<h1>500 - 服務器錯誤</h1>');}} else {// 發送文件res.writeHead(200, {'Content-Type': contentType});res.end(content);}});});
});// 啟動服務器
const PORT = 3000;
server.listen(PORT, () => {console.log(`靜態文件服務器運行在 http://localhost:${PORT}/`);console.log(`提供目錄: ${PUBLIC_DIR}`);
});
結語
感謝您的閱讀!期待您的一鍵三連!歡迎指正!