基础框架
概述
理解 Node.js 基础知识对于构建健壮的应用程序至关重要。本章介绍使 Node.js 独特的核心概念:事件循环、模块系统、流和非阻塞 I/O 模型。
Node.js 运行时架构
V8 JavaScript 引擎
Node.js 基于 Google 的 V8 引擎构建,该引擎将 JavaScript 编译为原生机器代码:
javascript
// performance-test.js
console.time('V8 Performance Test');
// CPU 密集型操作
function fibonacci(n) {
if (n < 2) return n;
return fibonacci(n - 1) + fibonacci(n - 2);
}
const result = fibonacci(35);
console.log('Fibonacci(35):', result);
console.timeEnd('V8 Performance Test');事件循环深入
事件循环是 Node.js 非阻塞架构的核心:
javascript
// event-loop-demo.js
console.log('=== Event Loop Phases ===');
// 1. 定时器阶段
setTimeout(() => console.log('Timer: setTimeout'), 0);
// 2. I/O 回调阶段
setImmediate(() => console.log('Check: setImmediate'));
// 3. 轮询阶段
process.nextTick(() => console.log('Next Tick: process.nextTick'));
// 4. 微任务 (Promises)
Promise.resolve().then(() => console.log('Microtask: Promise.resolve'));
console.log('Synchronous: Main thread');
// 输出顺序展示了事件循环阶段理解各个阶段
javascript
// event-loop-phases.js
const fs = require('fs');
console.log('Start');
// 定时器阶段
setTimeout(() => console.log('1: Timer'), 0);
setTimeout(() => console.log('2: Timer'), 0);
// I/O 回调阶段
fs.readFile(__filename, () => {
console.log('3: I/O callback');
// 检查阶段 (setImmediate)
setImmediate(() => console.log('4: setImmediate inside I/O'));
// Next tick (最高优先级)
process.nextTick(() => console.log('5: nextTick inside I/O'));
});
// 检查阶段
setImmediate(() => console.log('6: setImmediate'));
// Next tick 队列 (最高优先级)
process.nextTick(() => console.log('7: nextTick'));
console.log('End');模块系统
CommonJS 模块
Node.js 默认使用 CommonJS 模块系统:
javascript
// math-operations.js
const PI = 3.14159;
function calculateArea(radius) {
return PI * radius * radius;
}
function calculateCircumference(radius) {
return 2 * PI * radius;
}
class Calculator {
constructor() {
this.history = [];
}
add(a, b) {
const result = a + b;
this.history.push(`${a} + ${b} = ${result}`);
return result;
}
getHistory() {
return this.history;
}
}
// 不同的导出模式
module.exports = {
PI,
calculateArea,
calculateCircumference,
Calculator
};
// 替代导出语法
// exports.PI = PI;
// exports.calculateArea = calculateArea;使用模块:
javascript
// app.js
const { PI, calculateArea, Calculator } = require('./math-operations');
const mathOps = require('./math-operations');
console.log('PI value:', PI);
console.log('Circle area (radius 5):', calculateArea(5));
const calc = new Calculator();
console.log('Addition result:', calc.add(10, 15));
console.log('Calculator history:', calc.getHistory());ES6 模块 (ESM)
通过在 package.json 中添加 "type": "module" 来启用 ES6 模块:
javascript
// math-utils.mjs (or .js with "type": "module")
export const PI = 3.14159;
export function calculateArea(radius) {
return PI * radius * radius;
}
export default class Calculator {
constructor() {
this.operations = [];
}
multiply(a, b) {
const result = a * b;
this.operations.push({ operation: 'multiply', a, b, result });
return result;
}
}javascript
// main.mjs
import Calculator, { PI, calculateArea } from './math-utils.mjs';
import * as mathUtils from './math-utils.mjs';
console.log('Using named imports:', PI);
console.log('Area calculation:', calculateArea(3));
const calc = new Calculator();
console.log('Multiplication:', calc.multiply(4, 7));核心模块深入
文件系统 (fs) 模块
javascript
// file-operations.js
const fs = require('fs');
const path = require('path');
class FileManager {
constructor(baseDir = './data') {
this.baseDir = baseDir;
this.ensureDirectory();
}
ensureDirectory() {
if (!fs.existsSync(this.baseDir)) {
fs.mkdirSync(this.baseDir, { recursive: true });
}
}
// 同步操作(谨慎使用)
writeFileSync(filename, data) {
const filePath = path.join(this.baseDir, filename);
fs.writeFileSync(filePath, data, 'utf8');
return filePath;
}
// 异步操作(推荐)
async writeFile(filename, data) {
const filePath = path.join(this.baseDir, filename);
await fs.promises.writeFile(filePath, data, 'utf8');
return filePath;
}
async readFile(filename) {
const filePath = path.join(this.baseDir, filename);
return await fs.promises.readFile(filePath, 'utf8');
}
async listFiles() {
const files = await fs.promises.readdir(this.baseDir);
const fileStats = await Promise.all(
files.map(async (file) => {
const filePath = path.join(this.baseDir, file);
const stats = await fs.promises.stat(filePath);
return {
name: file,
size: stats.size,
isDirectory: stats.isDirectory(),
modified: stats.mtime
};
})
);
return fileStats;
}
async deleteFile(filename) {
const filePath = path.join(this.baseDir, filename);
await fs.promises.unlink(filePath);
}
}
// 使用示例
async function demonstrateFileOperations() {
const fileManager = new FileManager('./temp');
try {
// 写入文件
await fileManager.writeFile('test.txt', 'Hello, Node.js!');
await fileManager.writeFile('data.json', JSON.stringify({ message: 'Hello World' }, null, 2));
// 读取文件
const content = await fileManager.readFile('test.txt');
console.log('File content:', content);
// 列出文件
const files = await fileManager.listFiles();
console.log('Files in directory:');
files.forEach(file => {
console.log(`- ${file.name} (${file.size} bytes, ${file.isDirectory ? 'directory' : 'file'})`);
});
} catch (error) {
console.error('File operation error:', error.message);
}
}
demonstrateFileOperations();Path 模块
javascript
// path-operations.js
const path = require('path');
const os = require('os');
console.log('=== Path Operations ===');
// 路径连接
console.log('Join paths:', path.join('/users', 'john', 'documents', 'file.txt'));
console.log('Resolve path:', path.resolve('..', 'project', 'src'));
// 路径解析
const filePath = '/users/john/documents/report.pdf';
console.log('Parse path:', path.parse(filePath));
console.log('Directory name:', path.dirname(filePath));
console.log('Base name:', path.basename(filePath));
console.log('Extension:', path.extname(filePath));
// 跨平台路径
console.log('Platform separator:', path.sep);
console.log('Path delimiter:', path.delimiter);
// 规范化路径
console.log('Normalize:', path.normalize('/users//john/../jane/./documents'));
// 相对路径
console.log('Relative path:', path.relative('/users/john', '/users/jane/documents'));
// 检查路径是否为绝对路径
console.log('Is absolute:', path.isAbsolute('/users/john'));
console.log('Is absolute:', path.isAbsolute('./relative/path'));OS 模块
javascript
// system-info.js
const os = require('os');
function getSystemInfo() {
return {
platform: os.platform(),
architecture: os.arch(),
cpus: os.cpus().length,
totalMemory: Math.round(os.totalmem() / 1024 / 1024 / 1024 * 100) / 100 + ' GB',
freeMemory: Math.round(os.freemem() / 1024 / 1024 / 1024 * 100) / 100 + ' GB',
uptime: Math.round(os.uptime() / 3600 * 100) / 100 + ' hours',
hostname: os.hostname(),
userInfo: os.userInfo(),
networkInterfaces: Object.keys(os.networkInterfaces()),
homeDirectory: os.homedir(),
tempDirectory: os.tmpdir()
};
}
console.log('System Information:');
console.log(JSON.stringify(getSystemInfo(), null, 2));流
流是高效处理数据的强大功能:
可读流
javascript
// readable-stream.js
const { Readable } = require('stream');
const fs = require('fs');
// 自定义可读流
class NumberStream extends Readable {
constructor(options) {
super(options);
this.current = 0;
this.max = 10;
}
_read() {
if (this.current < this.max) {
this.push(`Number: ${this.current}\n`);
this.current++;
} else {
this.push(null); // 流结束
}
}
}
// 使用自定义流
const numberStream = new NumberStream();
numberStream.on('data', (chunk) => {
console.log('Received:', chunk.toString().trim());
});
numberStream.on('end', () => {
console.log('Stream ended');
});
// 文件流示例
const fileStream = fs.createReadStream('package.json');
fileStream.on('data', (chunk) => {
console.log(`Received ${chunk.length} bytes`);
});可写流
javascript
// writable-stream.js
const { Writable } = require('stream');
const fs = require('fs');
// 自定义可写流
class LogStream extends Writable {
constructor(options) {
super(options);
this.logFile = fs.createWriteStream('app.log', { flags: 'a' });
}
_write(chunk, encoding, callback) {
const timestamp = new Date().toISOString();
const logEntry = `[${timestamp}] ${chunk.toString()}`;
console.log('Writing to log:', logEntry.trim());
this.logFile.write(logEntry, callback);
}
}
// 使用自定义流
const logger = new LogStream();
logger.write('Application started\n');
logger.write('User logged in\n');
logger.write('Processing request\n');
logger.end();转换流
javascript
// transform-stream.js
const { Transform } = require('stream');
// 自定义转换流
class UpperCaseTransform extends Transform {
_transform(chunk, encoding, callback) {
const upperCased = chunk.toString().toUpperCase();
callback(null, upperCased);
}
}
// 管道示例
const fs = require('fs');
// 创建管道:读取文件 -> 转换为大写 -> 写入新文件
fs.createReadStream('input.txt')
.pipe(new UpperCaseTransform())
.pipe(fs.createWriteStream('output.txt'))
.on('finish', () => {
console.log('File transformation complete');
});Buffer 和二进制数据
javascript
// buffer-operations.js
console.log('=== Buffer Operations ===');
// 创建缓冲区
const buf1 = Buffer.from('Hello World', 'utf8');
const buf2 = Buffer.alloc(10); // 分配 10 字节
const buf3 = Buffer.allocUnsafe(10); // 更快但可能包含旧数据
console.log('Buffer from string:', buf1);
console.log('Buffer length:', buf1.length);
console.log('Buffer as string:', buf1.toString());
// 缓冲区操作
buf2.write('Node.js');
console.log('Written buffer:', buf2.toString());
// 缓冲区连接
const combined = Buffer.concat([buf1, Buffer.from(' - '), buf2]);
console.log('Combined buffer:', combined.toString());
// 二进制数据操作
const binaryData = Buffer.from([0x48, 0x65, 0x6c, 0x6c, 0x6f]); // "Hello" 的十六进制
console.log('Binary to string:', binaryData.toString());
// JSON 序列化
const jsonBuffer = Buffer.from(JSON.stringify({ message: 'Hello' }));
console.log('JSON buffer:', jsonBuffer);
console.log('Parsed JSON:', JSON.parse(jsonBuffer.toString()));错误处理模式
错误优先回调
javascript
// error-handling.js
const fs = require('fs');
// 传统回调模式
function readFileCallback(filename, callback) {
fs.readFile(filename, 'utf8', (error, data) => {
if (error) {
return callback(error, null);
}
callback(null, data);
});
}
// 使用
readFileCallback('nonexistent.txt', (error, data) => {
if (error) {
console.error('Callback error:', error.message);
return;
}
console.log('File data:', data);
});基于 Promise 的错误处理
javascript
// promise-errors.js
const fs = require('fs').promises;
async function safeFileOperation(filename) {
try {
const data = await fs.readFile(filename, 'utf8');
return { success: true, data };
} catch (error) {
return { success: false, error: error.message };
}
}
// Usage
safeFileOperation('package.json')
.then(result => {
if (result.success) {
console.log('File read successfully');
} else {
console.error('Error:', result.error);
}
});全局错误处理
javascript
// global-error-handling.js
// 处理未捕获的异常
process.on('uncaughtException', (error) => {
console.error('Uncaught Exception:', error);
process.exit(1);
});
// 处理未处理的 Promise 拒绝
process.on('unhandledRejection', (reason, promise) => {
console.error('Unhandled Rejection at:', promise, 'reason:', reason);
process.exit(1);
});
// 优雅关闭
process.on('SIGINT', () => {
console.log('Received SIGINT. Graceful shutdown...');
process.exit(0);
});
process.on('SIGTERM', () => {
console.log('Received SIGTERM. Graceful shutdown...');
process.exit(0);
});性能考虑
内存管理
javascript
// memory-management.js
function demonstrateMemoryUsage() {
const used = process.memoryUsage();
console.log('Memory Usage:');
for (let key in used) {
console.log(`${key}: ${Math.round(used[key] / 1024 / 1024 * 100) / 100} MB`);
}
}
console.log('Initial memory usage:');
demonstrateMemoryUsage();
// 创建大数组
const largeArray = new Array(1000000).fill('data');
console.log('\nAfter creating large array:');
demonstrateMemoryUsage();
// 清理
largeArray.length = 0;
global.gc && global.gc(); // 如果使用了 --expose-gc 标志,强制垃圾回收
console.log('\nAfter cleanup:');
demonstrateMemoryUsage();下一步
在下一章中,我们将探讨项目结构和组织模式,这些模式有助于构建可维护的 Node.js 应用程序。
练习
- 创建一个生成随机数的自定义可读流
- 使用 fs.watch() 构建一个记录文件更改的文件监视器
- 为聊天应用程序实现一个简单的事件发射器
- 为自定义文件格式创建一个基于缓冲区的数据解析器
要点
- 事件循环支持非阻塞 I/O 操作
- CommonJS 和 ES6 模块提供了不同的代码组织方式
- 核心模块 (fs、path、os) 提供基本的系统功能
- 流支持高效处理大型数据集
- 缓冲区有效处理二进制数据
- 适当的错误处理对于健壮的应用程序至关重要
- 理解内存管理有助于优化性能