Skip to content

基础框架

概述

理解 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 应用程序。

练习

  1. 创建一个生成随机数的自定义可读流
  2. 使用 fs.watch() 构建一个记录文件更改的文件监视器
  3. 为聊天应用程序实现一个简单的事件发射器
  4. 为自定义文件格式创建一个基于缓冲区的数据解析器

要点

  • 事件循环支持非阻塞 I/O 操作
  • CommonJS 和 ES6 模块提供了不同的代码组织方式
  • 核心模块 (fs、path、os) 提供基本的系统功能
  • 流支持高效处理大型数据集
  • 缓冲区有效处理二进制数据
  • 适当的错误处理对于健壮的应用程序至关重要
  • 理解内存管理有助于优化性能

本站内容仅供学习和研究使用。