强曰为道
与天地相似,故不违。知周乎万物,而道济天下,故不过。旁行而不流,乐天知命,故不忧.
文档目录

Node.js 开发指南 / 第 1 章 · Node.js 入门与概述

第 1 章 · Node.js 入门与概述

1.1 Node.js 简史

Node.js 由 Ryan Dahl2009 年 创建,最初目标是解决传统 Web 服务器(如 Apache)在高并发场景下的性能瓶颈。Ryan Dahl 发现,传统的"一个请求一个线程"模型在大量并发连接时会消耗大量内存和 CPU 资源。

关键里程碑

年份事件
2009Node.js 首次发布,基于 V8 引擎
2010npm(Node Package Manager)诞生
2011Windows 支持正式发布
2014io.js 分叉项目启动(后合并回 Node.js)
2015Node.js v4.0 发布,io.js 合并,Node.js Foundation 成立
2018Node.js 10 引入 fs.promises API
2020Node.js 14 LTS,ES Modules 实验性支持
2022Node.js 18 LTS,内置 fetchWeb Streams
2024Node.js 22 LTS,require(esm) 默认支持
2025Node.js 24 发布,持续增强 ESM 和性能

与 io.js 的合并

2014 年,部分核心开发者因不满 Node.js 的更新节奏,创建了 io.js 项目。2015 年,双方达成共识,合并为 Node.js v4.0,由 Node.js Foundation(后并入 OpenJS Foundation)管理。

1.2 V8 引擎

什么是 V8

V8 是 Google 开发的高性能 JavaScript 和 WebAssembly 引擎,最初为 Chrome 浏览器设计。Node.js 将 V8 引擎独立出来,使其可以在浏览器之外运行 JavaScript。

V8 的工作原理

JavaScript 源码
      │
      ▼
   解析器(Parser)
      │
      ▼
  抽象语法树(AST)
      │
      ▼
  解释器 Ignition → 字节码(Bytecode)
      │
      ▼
  编译器 TurboFan → 优化机器码(Optimized Machine Code)
      │
      ▼
  去优化(Deoptimization)← 类型假设失败时回退

V8 的关键特性

特性说明
JIT 编译Just-In-Time 编译,将热点代码编译为机器码
垃圾回收分代式 GC(Scavenge + Mark-Sweep-Compact)
隐藏类为对象创建隐藏类以优化属性访问
内联缓存缓存属性查找结果,加速重复访问
WebAssembly原生支持 WebAssembly 执行

在 Node.js 中查看 V8 版本

# 查看 Node.js 和 V8 版本
node -p "process.versions.v8"

# 完整版本信息
node -p "JSON.stringify(process.versions, null, 2)"

输出示例:

{
  "node": "22.12.0",
  "v8": "12.4.254.21-node.22",
  "uv": "1.49.2",
  "zlib": "1.3.0.2-motley-7d7a05e",
  "nghttp2": "1.61.0",
  "napi": "9",
  "llhttp": "9.2.1"
}

1.3 事件驱动与非阻塞 I/O

传统阻塞模型

传统的服务器(如 Apache)采用多线程阻塞 I/O 模型:

请求1 → 线程1 → [阻塞等待数据库] → 返回响应
请求2 → 线程2 → [阻塞等待文件读取] → 返回响应
请求3 → 线程3 → [阻塞等待网络请求] → 返回响应
...
请求N → 线程N → [阻塞等待] → 返回响应

问题:每个连接占用一个线程,线程切换开销大,内存消耗高。

Node.js 事件驱动模型

Node.js 采用单线程事件循环 + 非阻塞 I/O 模型:

请求1 → [注册回调] → 继续处理其他请求
请求2 → [注册回调] → 继续处理其他请求
请求3 → [注册回调] → 继续处理其他请求

I/O 完成 → 事件队列 → 事件循环取出 → 执行回调

单线程 vs 多线程对比

对比维度单线程(Node.js)多线程(Java/Apache)
内存占用低(单进程)高(每线程 1-8MB 栈空间)
上下文切换开销大
并发模型事件驱动线程池
CPU 密集型不擅长擅长
I/O 密集型非常擅长一般
编程复杂度低(回调/Promise)高(锁、同步)

代码示例:非阻塞 I/O

const fs = require('fs');

console.log('1. 开始读取文件');

// 非阻塞:注册回调后立即继续执行
fs.readFile('/etc/hosts', 'utf8', (err, data) => {
  if (err) throw err;
  console.log('3. 文件读取完成');
});

console.log('2. 文件读取已发起,但不会阻塞');

// 输出顺序:
// 1. 开始读取文件
// 2. 文件读取已发起,但不会阻塞
// 3. 文件读取完成

阻塞 vs 非阻塞对比

const fs = require('fs');

// 阻塞方式(不推荐在服务器中使用)
console.time('sync');
const data1 = fs.readFileSync('/etc/hosts', 'utf8');
console.timeEnd('sync'); // 约 1-3ms

// 非阻塞方式
console.time('async');
fs.readFile('/etc/hosts', 'utf8', (err, data2) => {
  console.timeEnd('async'); // 回调执行时记录
});
console.log('这里不会被阻塞');

1.4 适用场景与不适用场景

适用场景 ✅

场景说明典型框架/工具
I/O 密集型 Web 应用API 服务器、微服务Express, Fastify, Koa
实时应用聊天、协作编辑、推送Socket.io, ws
API 网关请求路由、负载均衡Express, Kong
流式处理音视频转码、数据管道ffmpeg, pipeline
CLI 工具命令行工具、脚本Commander, Inquirer
Serverless云函数、边缘计算AWS Lambda, Vercel
前端构建工具打包、编译、LintWebpack, Vite, ESLint
爬虫与数据采集网页抓取、数据处理Puppeteer, Cheerio

不太适用的场景 ⚠️

场景原因替代方案
CPU 密集型计算单线程会阻塞事件循环Go, Rust, Python (NumPy)
大型科学计算V8 内存限制C++, Fortran
系统级编程缺少底层访问Rust, C

解决 CPU 密集型问题

Node.js 也提供了应对 CPU 密集型任务的方案:

// 方案 1:Worker Threads(Node.js 10.5+)
const { Worker, isMainThread, parentPort } = require('worker_threads');

if (isMainThread) {
  // 主线程
  const worker = new Worker(__filename);
  worker.on('message', (result) => {
    console.log('计算结果:', result);
  });
  worker.postMessage({ numbers: [1, 2, 3, 4, 5] });
} else {
  // 工作线程
  parentPort.on('message', ({ numbers }) => {
    const sum = numbers.reduce((a, b) => a + b, 0);
    parentPort.postMessage(sum);
  });
}
// 方案 2:Cluster 模块(利用多核 CPU)
const cluster = require('cluster');
const http = require('http');
const numCPUs = require('os').cpus().length;

if (cluster.isPrimary) {
  console.log(`主进程 ${process.pid} 正在运行`);
  for (let i = 0; i < numCPUs; i++) {
    cluster.fork();
  }
  cluster.on('exit', (worker) => {
    console.log(`工作进程 ${worker.process.pid} 已退出`);
  });
} else {
  http.createServer((req, res) => {
    res.writeHead(200);
    res.end('Hello World\n');
  }).listen(8000);
  console.log(`工作进程 ${process.pid} 已启动`);
}

1.5 Node.js 架构全景

┌─────────────────────────────────────────────────┐
│                   应用代码                        │
│           (JavaScript / TypeScript)              │
├─────────────────────────────────────────────────┤
│               Node.js 标准库                      │
│   fs  http  net  crypto  stream  events  ...    │
├─────────────────────────────────────────────────┤
│              Node.js 绑定层(Bindings)            │
│            (C++ 与 JavaScript 桥接)               │
├──────────────────────┬──────────────────────────┤
│      V8 引擎         │       libuv              │
│  (JS 执行 & GC)      │  (异步 I/O 事件循环)      │
├──────────────────────┴──────────────────────────┤
│                 操作系统                          │
│          (文件系统 / 网络 / 进程)                  │
└─────────────────────────────────────────────────┘

核心组件说明

组件职责
V8解析和执行 JavaScript 代码
libuv跨平台异步 I/O 库,实现事件循环和线程池
llhttp高性能 HTTP 解析器
c-ares异步 DNS 解析
OpenSSLTLS/SSL 加密支持
zlib数据压缩

1.6 与其他运行时的对比

特性Node.jsDenoBun
JavaScript 引擎V8V8JavaScriptCore
包管理npm/yarn/pnpmURL 导入 / npm(2.0+)内置 bun install
TypeScript需要编译原生支持原生支持
安全模型无沙箱默认沙箱无沙箱
兼容性最成熟部分兼容大部分兼容
生态系统最丰富增长中增长中
性能优秀优秀极快(启动)

注意事项

⚠️ 单线程不等于单进程:Node.js 的主线程是单线程的,但可以通过 clusterworker_threads 或子进程利用多核 CPU。

⚠️ 避免阻塞事件循环:同步 I/O(如 fs.readFileSync)、JSON.parse 大对象、正则回溯等都会阻塞事件循环,影响所有并发请求。

⚠️ 错误处理很重要:单线程意味着未捕获的异常会导致整个进程崩溃,务必做好错误处理。

业务场景

  1. 电商平台 API 服务:高并发商品查询、订单处理,I/O 密集型场景非常适合 Node.js
  2. 实时聊天应用:WebSocket 长连接,事件驱动模型天然适合
  3. BFF(Backend For Frontend):为前端聚合多个微服务的数据
  4. CI/CD 工具链:ESLint、Prettier、Vite 等前端工具链均基于 Node.js

扩展阅读


下一章第 2 章 · 安装与环境配置 — 学习使用 nvm 管理 Node.js 版本,配置开发环境。