Node.js

技术栈
后端框架
javascriptruntimev8server-sideevent-drivennon-blocking

概览

Node.js 技术栈概览

Node.js 是一个基于 Chrome V8 引擎的 JavaScript 运行时环境,由 Ryan Dahl 于 2009 年创建。它让 JavaScript 从浏览器走向服务器端,采用事件驱动、非阻塞 I/O 模型,天然适合构建高并发的网络应用。

解决什么问题

  • 统一前后端语言:前端 JavaScript 开发者可直接编写后端代码
  • 高并发 I/O:单线程事件循环 + 异步非阻塞,轻松应对万级并发连接
  • 快速原型开发:npm 拥有全球最大的包生态(300万+ 包),毕设/毕设阶段能极速搭建
  • 全栈能力:Express/Koa/NestJS 做后端 + React/Vue 做前端,一人打通全栈

关键特性

  • V8 引擎高性能执行 JavaScript
  • 事件驱动 + libuv 线程池处理异步任务
  • npm 生态:Express、NestJS、Prisma、Socket.io 等
  • 支持 TypeScript,类型安全的大型项目开发
  • 跨平台,Windows/macOS/Linux 皆可运行

安装

环境准备

  • 操作系统:Windows 10+ / macOS 12+ / Ubuntu 20.04+
  • 运行时版本:Node.js 18 LTS 或 20 LTS(推荐 20 LTS,2026年前支持)
  • 依赖项:npm(随 Node.js 自带)、nvm(版本管理,推荐)

安装命令

Windows(安装程序)

  1. 访问 https://nodejs.org 下载 LTS 版本 .msi 安装包
  2. 双击运行,全部默认下一步即可
  3. 安装完成,打开 PowerShell 验证:
node -v    # v20.x.x
npm -v     # 10.x.x

macOS(nvm 推荐)

# 安装 nvm
curl -o- https://raw.githubusercontent.com/nvm-sh/nvm/v0.40.0/install.sh | bash
# 重启终端后
nvm install 20
nvm use 20
nvm alias default 20
node -v

Linux (Ubuntu)

# 使用 NodeSource 官方源
curl -fsSL https://deb.nodesource.com/setup_20.x | sudo -E bash -
sudo apt install -y nodejs
node -v &;& npm -v

淘宝镜像(国内加速)

npm config set registry https://registry.npmmirror.com

常见安装问题

问题 解决方案
npm install 报 EACCES 权限错误 不要用 sudo!改用 nvm 管理 Node.js,或设置 npm prefix:mkdir ~/.npm-global && npm config set prefix ~/.npm-global
node-gyp 编译失败 Windows 需要 npm install --global windows-build-tools;macOS 需 Xcode CLI:xcode-select --install
全局安装的包找不到 确认 npm global bin 在 PATH 中:npm bin -g 查看路径并加入环境变量
npm 下载慢/超时 切换国内镜像:npm config set registry https://registry.npmmirror.com

示例

Node.js Hello World — HTTP 服务器

目标

用 Node.js 内置 http 模块创建一个最简单的 Web 服务器,监听 3000 端口,返回 JSON 格式的 "Hello World"。

完整代码

// server.js
const http = require('http');

const hostname = '127.0.0.1';
const port = 3000;

const server = http.createServer((req, res) => {
  // 设置响应头
  res.statusCode = 200;
  res.setHeader('Content-Type', 'application/json');

  // 路由分发
  if (req.url === '/') {
    res.end(JSON.stringify({ message: 'Hello World', timestamp: Date.now() }));
  } else if (req.url === '/health') {
    res.end(JSON.stringify({ status: 'ok' }));
  } else {
    res.statusCode = 404;
    res.end(JSON.stringify({ error: 'Not Found' }));
  }
});

server.listen(port, hostname, () => {
  console.log(`Server running at http://${hostname}:${port}/`);
});

运行步骤

# 1. 保存上述代码为 server.js

# 2. 直接运行
node server.js

# 3. 另开终端测试
curl http://127.0.0.1:3000/
# 输出: {"message":"Hello World","timestamp":1717000000000}

预期输出

$ node server.js
Server running at http://127.0.0.1:3000/

访问 http://127.0.0.1:3000/ 返回:

{"message":"Hello World","timestamp":1717000000000}

要点说明

  • http.createServer() 创建 HTTP 服务器,传入回调处理每个请求
  • req.url 获取请求路径,可用于简单路由
  • res.setHeader() 设置响应头(JSON API 通常用 application/json
  • server.listen() 绑定端口并启动,第三个参数是启动回调

Node.js Express — RESTful API 增删改查

目标

使用 Express 框架构建一个完整的用户管理 RESTful API,实现 CRUD 操作,数据存储在内存中。

完整代码

// app.js
const express = require('express');
const app = express();
const port = 3000;

// 中间件:解析 JSON 请求体
app.use(express.json());

// 内存数据存储
let users = [
  { id: 1, name: 'Alice', email: 'alice@example.com' },
  { id: 2, name: 'Bob', email: 'bob@example.com' },
];
let nextId = 3;

// GET /users — 获取所有用户
app.get('/users', (req, res) => {
  res.json({ total: users.length, data: users });
});

// GET /users/:id — 获取单个用户
app.get('/users/:id', (req, res) => {
  const user = users.find(u => u.id === parseInt(req.params.id));
  if (!user) return res.status(404).json({ error: 'User not found' });
  res.json(user);
});

// POST /users — 创建用户
app.post('/users', (req, res) => {
  const { name, email } = req.body;
  if (!name || !email) {
    return res.status(400).json({ error: 'name and email are required' });
  }
  const user = { id: nextId++, name, email };
  users.push(user);
  res.status(201).json(user);
});

// PUT /users/:id — 更新用户
app.put('/users/:id', (req, res) => {
  const user = users.find(u => u.id === parseInt(req.params.id));
  if (!user) return res.status(404).json({ error: 'User not found' });
  const { name, email } = req.body;
  if (name) user.name = name;
  if (email) user.email = email;
  res.json(user);
});

// DELETE /users/:id — 删除用户
app.delete('/users/:id', (req, res) => {
  const index = users.findIndex(u => u.id === parseInt(req.params.id));
  if (index === -1) return res.status(404).json({ error: 'User not found' });
  users.splice(index, 1);
  res.status(204).send();
});

app.listen(port, () => {
  console.log(`API server listening at http://localhost:${port}`);
});

运行步骤

# 1. 初始化项目
npm init -y
npm install express

# 2. 保存上述代码为 app.js

# 3. 启动
node app.js

测试

# 获取所有用户
curl http://localhost:3000/users

# 创建用户
curl -X POST http://localhost:3000/users \
  -H "Content-Type: application/json" \
  -d '{"name":"Charlie","email":"charlie@example.com"}'

# 更新用户
curl -X PUT http://localhost:3000/users/1 \
  -H "Content-Type: application/json" \
  -d '{"name":"Alice Updated"}'

# 删除用户
curl -X DELETE http://localhost:3000/users/3

预期输出

$ node app.js
API server listening at http://localhost:3000
请求 响应
GET /users {"total":2,"data":[{...},{...}]}
POST /users {"id":3,"name":"Charlie","email":"charlie@example.com"}
PUT /users/1 {"id":1,"name":"Alice Updated","email":"alice@example.com"}
DELETE /users/3 204 No Content

要点说明

  • express.json() 中间件解析请求体 JSON
  • RESTful 命名规范:资源复数形式(/users),HTTP 方法表达操作
  • 状态码:200 成功、201 创建成功、204 删除成功无内容、400 参数错误、404 不存在

Node.js HTTP 服务器入门

目标

使用 Node.js 内置 http 模块和 fs 模块,创建最简单的 Web 服务器,并实现文件读写演示。

完整代码

// server.js
const http = require('http');
const fs = require('fs');
const path = require('path');

const PORT = 3000;
const DATA_FILE = path.join(__dirname, 'data.json');

// 初始化数据文件
if (!fs.existsSync(DATA_FILE)) {
  fs.writeFileSync(DATA_FILE, JSON.stringify({ visits: 0, messages: [] }, null, 2));
}

const server = http.createServer((req, res) => {
  const { method, url } = req;

  // 路由: GET / — 返回欢迎页
  if (method === 'GET' && url === '/') {
    const data = JSON.parse(fs.readFileSync(DATA_FILE, 'utf-8'));
    data.visits++;
    fs.writeFileSync(DATA_FILE, JSON.stringify(data, null, 2));

    res.writeHead(200, { 'Content-Type': 'text/html; charset=utf-8' });
    res.end(`
      <h1>🎉 Node.js 服务器运行中</h1>
      <p>累计访问次数:<strong>${data.visits}</strong></p>
      <p>消息数量:${data.messages.length}</p>
      <a href="/api/messages">查看消息 API</a>
    `);
  }

  // 路由: GET /api/messages — 返回 JSON
  else if (method === 'GET' && url === '/api/messages') {
    const data = JSON.parse(fs.readFileSync(DATA_FILE, 'utf-8'));
    res.writeHead(200, { 'Content-Type': 'application/json; charset=utf-8' });
    res.end(JSON.stringify({ success: true, ...data }));
  }

  // 路由: POST /api/messages — 新增消息
  else if (method === 'POST' && url === '/api/messages') {
    let body = '';
    req.on('data', chunk => (body += chunk));
    req.on('end', () => {
      const data = JSON.parse(fs.readFileSync(DATA_FILE, 'utf-8'));
      const { text } = JSON.parse(body);
      data.messages.push({ id: Date.now(), text, time: new Date().toISOString() });
      fs.writeFileSync(DATA_FILE, JSON.stringify(data, null, 2));
      res.writeHead(201, { 'Content-Type': 'application/json' });
      res.end(JSON.stringify({ success: true, message: '已添加' }));
    });
  }

  // 404
  else {
    res.writeHead(404, { 'Content-Type': 'application/json' });
    res.end(JSON.stringify({ error: 'Not Found' }));
  }
});

server.listen(PORT, () => {
  console.log(`🚀 服务器已启动: http://localhost:${PORT}`);
});

运行步骤

node server.js
# 浏览器访问 http://localhost:3000
# 测试 POST: curl -X POST http://localhost:3000/api/messages -H "Content-Type: application/json" -d '{"text":"Hello Node.js"}'

预期输出

  • 浏览器显示欢迎页,含访问计数
  • GET /api/messages 返回 JSON 消息列表
  • POST /api/messages 新增消息,data.json 文件实时更新

Node.js WebSocket 实时聊天室

目标

使用 ws 库构建多人在线聊天室,演示 Node.js 的事件驱动模型在实时通信中的优势。

完整代码

// server.js
const WebSocket = require('ws');
const http = require('http');
const fs = require('fs');
const path = require('path');

const server = http.createServer((req, res) => {
  if (req.url === '/') {
    res.writeHead(200, { 'Content-Type': 'text/html; charset=utf-8' });
    res.end(fs.readFileSync(path.join(__dirname, 'index.html'), 'utf-8'));
  }
});

const wss = new WebSocket.Server({ server });
const clients = new Map(); // ws -> username
let messageHistory = [];   // 保留最近 50 条

wss.on('connection', (ws, req) => {
  const ip = req.socket.remoteAddress;
  console.log(`🔗 新连接: ${ip}`);

  ws.on('message', (raw) => {
    const msg = JSON.parse(raw);

    switch (msg.type) {
      case 'join':
        clients.set(ws, msg.username);
        broadcast({ type: 'system', text: `${msg.username} 加入了聊天室`, time: now() });
        broadcast({ type: 'users', users: [...clients.values()] });
        // 发送历史消息
        ws.send(JSON.stringify({ type: 'history', messages: messageHistory }));
        break;

      case 'message':
        const username = clients.get(ws);
        if (username) {
          const chatMsg = { type: 'message', username, text: msg.text, time: now() };
          messageHistory.push(chatMsg);
          if (messageHistory.length > 50) messageHistory.shift();
          broadcast(chatMsg);
        }
        break;
    }
  });

  ws.on('close', () => {
    const username = clients.get(ws);
    if (username) {
      clients.delete(ws);
      broadcast({ type: 'system', text: `${username} 离开了`, time: now() });
      broadcast({ type: 'users', users: [...clients.values()] });
    }
  });
});

function broadcast(msg) {
  const data = JSON.stringify(msg);
  wss.clients.forEach((client) => {
    if (client.readyState === WebSocket.OPEN) client.send(data);
  });
}

function now() {
  return new Date().toLocaleTimeString('zh-CN', { hour12: false });
}

server.listen(3000, () => console.log('💬 聊天室: http://localhost:3000'));
<!-- index.html -->
<!DOCTYPE html>
<html><head><meta charset="utf-8"><title>聊天室</title>
<style>
  * { margin:0; padding:0; box-sizing:border-box; }
  body { font-family: system-ui; max-width:600px; margin:20px auto; }
  #messages { border:1px solid #ddd; height:400px; overflow-y:auto; padding:10px; }
  .system { color:#999; text-align:center; margin:5px 0; }
  .msg { margin:4px 0; } .msg strong { color:#2563eb; }
  input,button { padding:8px; margin-top:8px; }
  input { width:70%; } button { width:25%; }
</style></head><body>
<h2>💬 在线聊天室</h2>
<div id="messages"></div>
<input id="msgInput" placeholder="输入消息..." autofocus>
<button onclick="sendMsg()">发送</button>

<script>
  const username = '用户' + Math.floor(Math.random()*1000);
  const ws = new WebSocket(`ws://${location.host}`);
  const messages = document.getElementById('messages');
  const msgInput = document.getElementById('msgInput');

  ws.onopen = () => ws.send(JSON.stringify({ type:'join', username }));

  ws.onmessage = (e) => {
    const msg = JSON.parse(e.data);
    if (msg.type === 'system') {
      messages.innerHTML += `<div class="system">${msg.text} <small>${msg.time}</small></div>`;
    } else if (msg.type === 'message') {
      messages.innerHTML += `<div class="msg"><strong>${msg.username}</strong>: ${msg.text} <small>${msg.time}</small></div>`;
    }
    messages.scrollTop = messages.scrollHeight;
  };

  function sendMsg() {
    const text = msgInput.value.trim();
    if (text) {
      ws.send(JSON.stringify({ type:'message', text }));
      msgInput.value = '';
    }
  }
  msgInput.addEventListener('keydown', (e) => { if(e.key==='Enter') sendMsg(); });
</script></body></html>

运行步骤

npm install ws
node server.js
# 打开多个浏览器窗口访问 http://localhost:3000

预期效果

  • 每个窗口输入用户名后加入
  • 消息实时推送给所有在线用户
  • 用户离开时全网广播
  • 新接入用户收到最近历史消息

教程

Node.js 入门篇:从零搭建 RESTful API

背景

Node.js 是最流行的后端 JavaScript 运行时。本教程带你从零搭建一个完整的 RESTful API 服务,涵盖路由、中间件、数据库操作。

核心概念

1. 模块系统

Node.js 使用 CommonJS(require / module.exports),也支持 ES Modules(import / export,需 .mjspackage.json"type":"module")。

// CommonJS
const express = require('express');
module.exports = myFunction;

// ES Modules
import express from 'express';
export default myFunction;

2. 事件循环(Event Loop)

Node.js 单线程通过事件循环处理异步操作。理解宏任务(setTimeout)和微任务(Promise)的执行顺序是调试的关键。

3. 中间件模式

Express/Koa 的核心思想:请求依次经过多个中间件,每个中间件可:

  • 执行代码
  • 修改 req/res 对象
  • 结束请求(res.send()
  • 调用下一个中间件(next()

分步操作

第一步:初始化项目

mkdir my-api &;& cd my-api
npm init -y
npm install express cors morgan
npm install -D nodemon

第二步:基础服务器

const express = require('express');
const app = express();
const PORT = process.env.PORT || 3000;

app.use(express.json());
app.listen(PORT, () => console.log(`Server on port ${PORT}`));

第三步:路由与控制器分离

src/
  routes/
    users.js
  controllers/
    usersController.js
  models/
    user.js
  app.js

第四步:集成数据库(SQLite)

npm install better-sqlite3
const Database = require('better-sqlite3');
const db = new Database('app.db');
db.exec(`CREATE TABLE IF NOT EXISTS users (
  id INTEGER PRIMARY KEY AUTOINCREMENT,
  name TEXT NOT NULL,
  email TEXT UNIQUE NOT NULL
)`);

第五步:错误处理中间件

// 统一错误处理 — 放在所有路由之后
app.use((err, req, res, next) => {
  console.error(err.stack);
  res.status(500).json({ error: 'Internal Server Error' });
});

项目最终结构

my-api/
├── package.json
├── src/
│   ├── app.js          # Express 应用入口
│   ├── db.js           # 数据库连接
│   ├── routes/
│   │   └── users.js    # 用户路由
│   ├── controllers/
│   │   └── usersController.js
│   └── middleware/
│       └── errorHandler.js
└── app.db              # SQLite 数据文件

思考题

  1. 如果 10,000 个请求同时到达,Node.js 单线程如何处理?
  2. 中间件 app.use() 的顺序为什么重要?如果错误处理中间件放在路由之前会怎样?
  3. CommonJS 和 ES Modules 在循环依赖处理上有何不同?

Node.js 进阶篇:异步编程与性能优化

背景

Node.js 的异步模型是其性能核心。理解 Promise、async/await、Worker Threads 以及 Stream,是编写高性能 Node.js 应用的关键。

核心概念

1. Promise 链与 async/await

// 回调地狱 ❌
fs.readFile('a.txt', (err, dataA) => {
  fs.readFile('b.txt', (err, dataB) => {
    fs.readFile('c.txt', (err, dataC) => {
      console.log(dataA + dataB + dataC);
    });
  });
});

// Promise 链 ✅
Promise.all([
  fs.promises.readFile('a.txt'),
  fs.promises.readFile('b.txt'),
  fs.promises.readFile('c.txt'),
]).then(buffers => console.log(buffers.join('')));

// async/await 最优雅 ✅
const [a, b, c] = await Promise.all([
  fs.promises.readFile('a.txt'),
  fs.promises.readFile('b.txt'),
  fs.promises.readFile('c.txt'),
]);

2. Stream — 流式处理大数据

const fs = require('fs');
const zlib = require('zlib');

// 边读边压缩边写 — 内存占用恒定
fs.createReadStream('large.log')
  .pipe(zlib.createGzip())
  .pipe(fs.createWriteStream('large.log.gz'));

3. Worker Threads — 突破单线程限制

// main.js
const { Worker } = require('worker_threads');

function runCPUIntensiveTask(data) {
  return new Promise((resolve, reject) => {
    const worker = new Worker('./worker.js', { workerData: data });
    worker.on('message', resolve);
    worker.on('error', reject);
  });
}

// worker.js
const { parentPort, workerData } = require('worker_threads');
// 执行 CPU 密集运算
let result = fibonacci(workerData.n);
parentPort.postMessage(result);

4. Cluster — 利用多核 CPU

const cluster = require('cluster');
const os = require('os');

if (cluster.isPrimary) {
  os.cpus().forEach(() => cluster.fork());
  cluster.on('exit', (worker) => cluster.fork()); // 自动重启
} else {
  require('./app'); // 启动 Express 服务
}

性能优化检查清单

优化项 方法
数据库查询 使用连接池、添加索引、避免 N+1 查询
缓存 Redis 缓存热点数据、设置合理 TTL
压缩 启用 gzip/br 压缩响应
静态资源 使用 CDN 或 Nginx 反代
内存泄漏 使用 --inspect + Chrome DevTools 排查
事件循环阻塞 避免同步 I/O、大循环拆分为 setImmediate

分步操作:压测与排查

# 1. 使用 autocannon 压测
npm i -g autocannon
autocannon -c 100 -d 10 http://localhost:3000/users

# 2. 使用 clinic.js 诊断性能
npm i -g clinic
clinic doctor -- node app.js
# 然后运行 autocannon,分析生成的 HTML 报告

# 3. 查找耗时中间件
app.use((req, res, next) =>; {
  const start = Date.now();
  res.on('finish', () =>; {
    console.log(`${req.method} ${req.url} ${Date.now() - start}ms`);
  });
  next();
});

思考题

  1. Promise.allPromise.allSettled 有什么区别?什么场景用哪个?
  2. 为什么 Worker Threads 不适合处理大量 I/O 操作?
  3. Stream 的背压(backpressure)机制是如何工作的?

Node.js 毕设实战教程

前言

Node.js 是大学毕设的热门选择,因为它一门语言打穿前后端。本教程将带你从零构建一个完整的毕设后端。

第一章:理解事件循环

Node.js 的核心是 事件循环(Event Loop)。与传统的多线程模型不同,Node.js 用单线程处理所有请求:

  请求1 ──→ [Event Loop] ──→ 非阻塞I/O ──→ 回调1
  请求2 ──→               ──→ 非阻塞I/O ──→ 回调2
  请求3 ──→               ──→ CPU任务    ──→ 回调3

关键理解:CPU 密集任务会阻塞事件循环!一个大循环会让所有请求卡住。所以 Node.js 擅长 I/O 密集型(API 网关、实时聊天),不擅长视频编码、图像渲染。

// ❌ 阻塞事件循环
app.get('/slow', (req, res) => {
  for (let i = 0; i < 1e10; i++) {} // 所有请求都卡住
  res.send('done');
});

// ✅ 异步非阻塞
app.get('/fast', async (req, res) => {
  const data = await fetchDataFromDB(); // 不阻塞
  res.json(data);
});

第二章:项目结构最佳实践

毕设项目推荐的分层结构:

my-project/
├── src/
│   ├── controllers/    # 处理 HTTP 请求
│   ├── services/       # 业务逻辑
│   ├── models/         # 数据模型(Mongoose/Prisma)
│   ├── middlewares/    # 认证、日志、错误处理
│   ├── routes/         # 路由定义
│   ├── utils/          # 工具函数
│   └── app.js          # Express 实例
├── tests/
├── .env                # 环境变量
└── package.json

为什么这么分?

  • Controller 只负责取参数、调 Service、返回响应
  • Service 包含所有业务逻辑,可复用、可测试
  • Model 定义数据结构,与数据库交互

第三章:认证系统(JWT)

毕设中几乎每个项目都需要登录。JWT 是最简单的方案:

const jwt = require('jsonwebtoken');

// 登录 — 签发 Token
app.post('/login', (req, res) => {
  const { username, password } = req.body;
  // 验证账号密码(略)
  const token = jwt.sign(
    { userId: user.id, role: user.role },
    process.env.JWT_SECRET,
    { expiresIn: '7d' }
  );
  res.json({ token });
});

// 中间件 — 验证 Token
const authMiddleware = (req, res, next) => {
  const token = req.headers.authorization?.split(' ')[1];
  if (!token) return res.status(401).json({ error: '请先登录' });
  try {
    req.user = jwt.verify(token, process.env.JWT_SECRET);
    next();
  } catch {
    res.status(401).json({ error: 'Token 已过期' });
  }
};

// 保护路由
app.get('/profile', authMiddleware, (req, res) => {
  res.json({ userId: req.user.userId });
});

第四章:数据库选择指南

数据库 适用场景 Node.js ORM
MongoDB 灵活文档、快速原型 Mongoose
PostgreSQL 关系型、复杂查询 Prisma / TypeORM
SQLite 毕设演示、单文件 better-sqlite3
MySQL 传统 Web 应用 Prisma / Sequelize

推荐:毕设用 SQLite + Prisma,零配置,答辩时直接复制 .db 文件即可演示。

第五章:部署上线

# 使用 PM2 进程管理
npm install -g pm2
pm2 start src/app.js --name my-api
pm2 save
pm2 startup   # 开机自启

思考题

  1. 如果有个请求需要处理 10 秒,如何避免阻塞其他请求?
  2. JWT Token 存在前端哪里最安全?(提示:Cookie vs localStorage)
  3. 10 万用户同时在线,Node.js 单进程够用吗?(提示:PM2 cluster mode)

参考资料

  1. [1] OpenJS Foundation. Node.js 官方文档. 2024.
  2. [2] Mario Casciaro, Luciano Mammino. Node.js Design Patterns. 2020.
  3. [3] Bryan Hughes. The Node.js Event Loop: Not So Single Threaded. 2019.
  4. [4] James Halliday (substack). Stream Handbook. 2015.
  5. [5] OpenJS Foundation. Node.js 官方文档. 2024.
  6. [6] Mario Casciaro, Luciano Mammino. Node.js Design Patterns. 2020.
  7. [7] Bryan Hughes. The Node.js Event Loop: Not So Single Threaded. 2024.
  8. [8] OpenJS Foundation. Express.js 官方文档. 2024.
  9. [9] Mario Casciaro, Luciano Mammino. Node.js Design Patterns. 2020.
  10. [10] OpenJS Foundation. Node.js 官方文档. 2024. https://nodejs.org/docs/latest/
  11. [11] Bert Belder. The Event Loop in Node.js. 2016. https://www.youtube.com/watch?v=PNa9OMajw9w
  12. [12] yjhjstz. 深入理解 Node.js:核心思想与源码分析. 2018. https://github.com/yjhjstz/deep-into-node