Node.js 知识点梳理与实战代码

Node.js 学习文档


1. Node 基础

1.1 基础概念

定义与特点

Node.js 是一个基于 Chrome V8 引擎 的 JavaScript 运行时环境,让 JavaScript 可以运行在服务端。

核心特点:

  • 异步非阻塞 I/O
  • 事件驱动架构
  • 单线程模型
  • 跨平台(Windows / macOS / Linux)
  • 拥有丰富的 npm 生态

事件驱动(Event-Driven)

Node.js 使用 事件循环(Event Loop) 来处理并发,所有异步操作完成后通过触发"事件"来执行回调函数。

js 复制代码
// 示例:events/event-driven.js
const EventEmitter = require('events');

const emitter = new EventEmitter();

// 注册事件监听
emitter.on('greet', (name) => {
  console.log(`Hello, ${name}!`);
});

// 触发事件
emitter.emit('greet', 'Node.js');
// 输出:Hello, Node.js!

非阻塞 I/O(Non-Blocking I/O)

传统同步模型中,I/O 操作(读文件、网络请求)会阻塞线程。Node.js 将 I/O 操作交给底层(libuv),完成后回调通知主线程,主线程无需等待。

js 复制代码
// 示例:basic/non-blocking.js
const fs = require('fs');

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

// 非阻塞读取
fs.readFile('./package.json', 'utf8', (err, data) => {
  if (err) {
    console.error('读取失败:', err.message);
    return;
  }
  console.log('3. 文件内容长度:', data.length);
});

console.log('2. 读取请求已发出,继续执行其他代码');

// 输出顺序:1 → 2 → 3(不会阻塞)

单线程模型

核心理解: Node.js 的"单线程"指的是 JavaScript 执行线程只有一个。但 Node.js 底层的 libuv 库维护了一个线程池(默认 4 个线程),专门处理文件 I/O、DNS 查询、加密等耗时操作,主线程不会被阻塞。

css 复制代码
         ┌──────────────────────────────────────────┐
         │           Node.js 进程                    │
         │                                          │
         │  ┌───────────────────────────────────┐   │
         │  │     JS 主线程(单线程)             │   │
         │  │  - 执行 JavaScript 代码             │   │
         │  │  - 运行事件循环                     │   │
         │  │  - 处理回调函数                     │   │
         │  └────────────┬──────────────────────┘   │
         │               │ 异步任务(I/O、加密等)    │
         │  ┌────────────▼──────────────────────┐   │
         │  │       libuv 线程池(默认4线程)     │   │
         │  │  线程1 │ 线程2 │ 线程3 │ 线程4     │   │
         │  │  文件IO  DNS   加密    压缩         │   │
         │  └────────────┬──────────────────────┘   │
         │               │ 任务完成 → 放入事件队列    │
         │  ┌────────────▼──────────────────────┐   │
         │  │           事件队列                 │   │
         │  │  [回调1] [回调2] [回调3] ...       │   │
         │  └────────────┬──────────────────────┘   │
         │               │ 事件循环取出并执行          │
         │               └──→ JS 主线程              │
         └──────────────────────────────────────────┘

单线程的好处:

  • 无需处理多线程的锁、竞态条件、死锁等问题
  • 内存共享安全,无需同步原语
  • 上下文切换开销极小

单线程的限制:

  • CPU 密集型任务(大量计算、图像处理)会阻塞主线程,导致所有请求挂起
  • 单个未捕获异常会导致整个进程崩溃

验证单线程阻塞:

js 复制代码
// 示例:basic/single-thread.js
const http = require('http');

const server = http.createServer((req, res) => {
  if (req.url === '/block') {
    // 模拟 CPU 密集型阻塞(3秒计算)
    const start = Date.now();
    while (Date.now() - start < 3000) {} // 死循环阻塞主线程
    res.end('阻塞结束');
  } else {
    res.end('正常响应');
  }
});

server.listen(3000, () => {
  console.log('服务启动: http://localhost:3000');
  console.log('访问 /block 会阻塞所有其他请求 3 秒');
});
// 测试:同时访问 /block 和 /,会发现 / 也被阻塞

解决 CPU 密集型任务的方案:

js 复制代码
// 方案:worker_threads(Node.js 10.5+)
// 示例:basic/worker-demo.js
const { Worker, isMainThread, parentPort } = require('worker_threads');

// 执行流程:
// 1. node worker-demo.js → 主线程启动,isMainThread = true → 进入 if 分支
// 2. new Worker(__filename) → 用同一个文件再开一个新线程
// 3. 新线程中重新执行本文件,此时 isMainThread = false → 进入 else 分支
// 4. Worker 计算完成后通过 parentPort.postMessage 发消息给主线程
// 5. 主线程 worker.on('message') 收到消息并打印结果

if (isMainThread) {
  // ← 主线程执行此分支(node worker-demo.js 时)
  const worker = new Worker(__filename); // 让同一文件在新线程中运行
  worker.on('message', (result) => {
    console.log('计算结果:', result); // 主线程不阻塞
  });
  console.log('主线程继续运行,不被 Worker 阻塞');
} else {
  // ← Worker 线程执行此分支(被 new Worker(__filename) 启动时)
  let sum = 0;
  for (let i = 0; i < 1e8; i++) sum += i;
  parentPort.postMessage(sum); // 将结果发送给主线程
}

线程池大小配置:

bash 复制代码
# 通过环境变量调整 libuv 线程池大小(最大 1024)
UV_THREADPOOL_SIZE=8 node app.js

哪些操作走线程池,哪些走系统内核:

操作类型 处理方式
文件读写(fs) libuv 线程池
DNS 解析 libuv 线程池
crypto 加密 libuv 线程池
zlib 压缩 libuv 线程池
TCP/UDP 网络 系统内核异步(epoll/kqueue/IOCP)
HTTP 请求 系统内核异步

事件循环(Event Loop)

事件循环是 Node.js 实现非阻塞 I/O 的核心机制,按以下阶段循环执行:

arduino 复制代码
   ┌───────────────────────────┐
┌─>│        timers             │  setTimeout / setInterval 回调
│  └─────────────┬─────────────┘
│  ┌─────────────▼─────────────┐
│  │     pending callbacks     │  上一轮延迟的 I/O 回调
│  └─────────────┬─────────────┘
│  ┌─────────────▼─────────────┐
│  │       idle, prepare       │  内部使用
│  └─────────────┬─────────────┘
│  ┌─────────────▼─────────────┐
│  │           poll            │  获取新 I/O 事件
│  └─────────────┬─────────────┘
│  ┌─────────────▼─────────────┐
│  │           check           │  setImmediate 回调
│  └─────────────┬─────────────┘
│  ┌─────────────▼─────────────┐
└──│      close callbacks      │  关闭事件回调
   └───────────────────────────┘
js 复制代码
// 示例:basic/event-loop.js
console.log('1. 同步代码开始');

setTimeout(() => console.log('4. setTimeout'), 0);

Promise.resolve().then(() => console.log('3. Promise microtask'));

setImmediate(() => console.log('5. setImmediate'));

console.log('2. 同步代码结束');

// 输出顺序:1 → 2 → 3 → 5 → 4
// 微任务(Promise)优先于宏任务(setTimeout、setImmediate)
// setImmediate 与 setTimeout(0) 的执行先后取决于运行上下文,通常 setImmediate 更快

1.2 安装和配置

Node.js 安装

方式一:官网下载

  • 访问 nodejs.org
  • 推荐下载 LTS 版本(长期支持)

方式二:nvm 管理多版本(推荐)

bash 复制代码
# Windows 使用 nvm-windows
# 安装后使用:
nvm install 20       # 安装 Node.js 20
nvm use 20           # 切换版本
nvm list             # 查看已安装版本

# 验证安装
node -v              # 查看 Node 版本
npm -v               # 查看 npm 版本

npm 使用

npm(Node Package Manager)是 Node.js 的默认包管理器。

bash 复制代码
# 查看 npm 版本
npm -v

# 查看全局安装路径
npm root -g

# 更新 npm 自身
npm install -g npm@latest

# 配置镜像源(国内加速)
npm config set registry https://registry.npmmirror.com

# 查看当前镜像
npm config get registry

package.json

package.json 是项目的配置清单,记录项目信息、依赖、脚本等。

json 复制代码
// 示例:package.json
{
  "name": "node-demo",
  "version": "1.0.0",
  "description": "Node.js 学习示例",
  "main": "index.js",
  "scripts": {
    "start": "node index.js",
    "dev": "nodemon index.js",
    "test": "node test/index.test.js"
  },
  "keywords": ["nodejs", "demo"],
  "author": "Your Name",
  "license": "MIT",
  "dependencies": {
    "express": "^4.18.2"
  },
  "devDependencies": {
    "nodemon": "^3.0.0"
  }
}

字段说明:

字段 说明
name 包名(小写,无空格)
version 版本号(遵循 semver:主.次.补丁)
main 入口文件
scripts 自定义脚本命令
dependencies 生产依赖
devDependencies 开发依赖(不打包进生产)

1.3 模块系统

CommonJS 模块(CJS)

Node.js 默认模块系统,使用 require 引入、module.exports 导出。

js 复制代码
// 示例:modules/cjs/math.js(导出)
const PI = 3.14159;

function add(a, b) {
  return a + b;
}

function multiply(a, b) {
  return a * b;
}

// 导出多个
module.exports = { PI, add, multiply };
js 复制代码
// 示例:modules/cjs/main.js(引入)
const { PI, add, multiply } = require('./math');

console.log('PI =', PI);
console.log('3 + 4 =', add(3, 4));
console.log('3 × 4 =', multiply(3, 4));

特性:

  • 同步加载(适合服务端)
  • require 有缓存机制,同一模块只加载一次
  • __dirname:当前文件所在目录
  • __filename:当前文件完整路径
js 复制代码
// 示例:modules/cjs/path-demo.js
console.log('当前目录:', __dirname);
console.log('当前文件:', __filename);

ES Module(ESM)

ES6 标准模块系统,使用 import / export,Node.js 12+ 支持。

启用方式:

  • 文件扩展名改为 .mjs,或
  • package.json 中设置 "type": "module"
js 复制代码
// 示例:modules/esm/math.mjs(导出)
export const PI = 3.14159;

export function add(a, b) {
  return a + b;
}

// 默认导出
export default function subtract(a, b) {
  return a - b;
}
js 复制代码
// 示例:modules/esm/main.mjs(引入)
import subtract, { PI, add } from './math.mjs';

console.log('PI =', PI);
console.log('3 + 4 =', add(3, 4));
console.log('10 - 3 =', subtract(10, 3));

// 动态导入
const { multiply } = await import('./math.mjs').catch(() => ({ multiply: null }));

CJS vs ESM 对比:

特性 CommonJS ES Module
语法 require / module.exports import / export
加载时机 运行时(同步) 编译时(静态分析)
动态导入 原生支持 import() 动态语法
this 指向 module.exports undefined
适用场景 Node.js 传统项目 现代项目、前端打包

内置模块

Node.js 自带的模块,无需安装,直接 require

js 复制代码
// 示例:modules/builtin/builtin-demo.js
const path = require('path');
const os = require('os');
const fs = require('fs');
const { URL } = require('url');

// path:路径处理
console.log('--- path ---');
console.log(path.join('/user', 'data', 'file.txt'));     // /user/data/file.txt
console.log(path.extname('index.html'));                  // .html
console.log(path.basename('/user/data/file.txt'));        // file.txt
console.log(path.dirname('/user/data/file.txt'));         // /user/data
console.log(path.resolve('src', 'index.js'));             // 绝对路径

// os:操作系统信息
console.log('\n--- os ---');
console.log('平台:', os.platform());
console.log('CPU核心数:', os.cpus().length);
console.log('总内存(GB):', (os.totalmem() / 1024 ** 3).toFixed(2));
console.log('空闲内存(GB):', (os.freemem() / 1024 ** 3).toFixed(2));
console.log('主机名:', os.hostname());

// url:URL 解析
console.log('\n--- url ---');
const myUrl = new URL('https://example.com:8080/path?name=node&v=20#section');
console.log('协议:', myUrl.protocol);
console.log('主机:', myUrl.host);
console.log('路径:', myUrl.pathname);
console.log('参数name:', myUrl.searchParams.get('name'));

常用内置模块:

模块 用途
fs 文件系统操作
path 路径处理
http / https 创建 HTTP 服务
os 操作系统信息
events 事件发射器
stream 流处理
url URL 解析
crypto 加密功能
util 实用工具
child_process 子进程

第三方模块

通过 npm 安装,存放于 node_modules 目录。

bash 复制代码
# 安装示例
npm install lodash
npm install axios
js 复制代码
// 示例:modules/third-party/third-party-demo.js
// 需先执行:npm install lodash
const _ = require('lodash');

const arr = [1, 2, 3, 4, 5];
console.log('sum:', _.sum(arr));           // 15
console.log('max:', _.max(arr));           // 5
console.log('chunk:', _.chunk(arr, 2));    // [[1,2],[3,4],[5]]

const obj = { a: 1, b: { c: 2 } };
const clone = _.cloneDeep(obj);
clone.b.c = 99;
console.log('原始:', obj.b.c);            // 2(深拷贝不影响原对象)
console.log('克隆:', clone.b.c);          // 99

自定义模块

将业务逻辑封装为模块,实现代码复用。

js 复制代码
// 示例:modules/custom/logger.js(自定义日志模块)
const LOG_LEVELS = { INFO: 'INFO', WARN: 'WARN', ERROR: 'ERROR' };

function formatMessage(level, message) {
  const time = new Date().toISOString();
  return `[${time}] [${level}] ${message}`;
}

function info(message) {
  console.log(formatMessage(LOG_LEVELS.INFO, message));
}

function warn(message) {
  console.warn(formatMessage(LOG_LEVELS.WARN, message));
}

function error(message) {
  console.error(formatMessage(LOG_LEVELS.ERROR, message));
}

module.exports = { info, warn, error };
js 复制代码
// 示例:modules/custom/main.js(使用自定义模块)
const logger = require('./logger');

logger.info('应用已启动');
logger.warn('内存使用率较高');
logger.error('数据库连接失败');

1.4 npm 包管理

npm init

初始化项目,生成 package.json

bash 复制代码
# 交互式初始化(逐步填写)
npm init

# 快速初始化(全部使用默认值)
npm init -y

# 使用自定义默认值
npm config set init-author-name "Your Name"
npm config set init-license "MIT"
npm init -y

npm install

安装依赖包。

bash 复制代码
# 安装所有依赖(根据 package.json)
npm install
npm i                            # 简写

# 安装指定包(生产依赖)
npm install express
npm install express@4.18.2       # 指定版本
npm install express@latest       # 最新版

# 安装为开发依赖(不打包生产)
npm install nodemon --save-dev
npm install nodemon -D           # 简写

# 全局安装(命令行工具)
npm install -g nodemon
npm install -g typescript

# 卸载包
npm uninstall express
npm un express                   # 简写

# 更新包
npm update express               # 更新到兼容最新版
npm update                       # 更新所有

# 查看已安装包
npm list
npm list --depth=0               # 只看顶层依赖
npm list -g --depth=0            # 全局已安装

版本符号说明(semver):

json 复制代码
"express": "^4.18.2"   → 兼容 4.x.x,不升级主版本
"express": "~4.18.2"   → 兼容 4.18.x,不升级次版本
"express": "4.18.2"    → 精确版本
"express": "*"         → 任意版本(不推荐)

npm scripts

package.jsonscripts 字段定义可执行脚本。

json 复制代码
{
  "scripts": {
    "start": "node src/index.js",
    "dev": "nodemon src/index.js",
    "build": "tsc",
    "test": "node test/index.test.js",
    "lint": "eslint src/**/*.js",
    "clean": "rm -rf dist",
    "prebuild": "npm run clean",
    "postbuild": "echo Build complete!"
  }
}
bash 复制代码
# 运行脚本
npm run start
npm start           # start / stop / test 可省略 run
npm test
npm run dev

# 传递参数(-- 后的参数会传入脚本)
npm run dev -- --port 3000

# 查看所有脚本
npm run

生命周期钩子:

  • pre<script>:在指定脚本前自动执行(如 prebuild
  • post<script>:在指定脚本后自动执行(如 postbuild

yarn / pnpm

yarn(Facebook 出品,更快更稳定):

bash 复制代码
# 安装 yarn
npm install -g yarn

# 常用命令对比
yarn init          # = npm init
yarn               # = npm install
yarn add express   # = npm install express
yarn add -D nodemon # = npm install -D nodemon
yarn remove express # = npm uninstall express
yarn run dev       # = npm run dev
yarn global add nodemon  # = npm install -g nodemon

# yarn.lock 锁定版本(提交到 git)

pnpm(磁盘高效,monorepo 友好):

bash 复制代码
# 安装 pnpm
npm install -g pnpm

# 常用命令对比
pnpm init          # = npm init
pnpm install       # = npm install
pnpm add express   # = npm install express
pnpm add -D nodemon # = npm install -D nodemon
pnpm remove express # = npm uninstall express
pnpm run dev       # = npm run dev

# pnpm-lock.yaml 锁定版本

三者对比:

特性 npm yarn pnpm
安装速度 一般 最快
磁盘占用 低(硬链接共享)
lockfile package-lock.json yarn.lock pnpm-lock.yaml
monorepo 支持(workspaces) 支持(workspaces) 原生最优
安全性 一般

2. Node.js 核心模块

2.1 文件系统 fs

读取文件
js 复制代码
// core/fs/read-file.js
const fs = require('fs');
const path = require('path');

const filePath = path.join(__dirname, 'demo.txt');

// 异步读取(推荐)
fs.readFile(filePath, 'utf8', (err, data) => {
  if (err) return console.error('读取失败:', err.message);
  console.log('异步读取:', data);
});

// 同步读取(会阻塞,慎用)
try {
  const data = fs.readFileSync(filePath, 'utf8');
  console.log('同步读取:', data);
} catch (err) {
  console.error('读取失败:', err.message);
}

// Promise 方式(推荐,Node.js 10+)
const fsPromises = require('fs').promises;

async function readDemo() {
  const data = await fsPromises.readFile(filePath, 'utf8');
  console.log('Promise读取:', data);
}
readDemo().catch(console.error);
写入文件
js 复制代码
// core/fs/write-file.js
const fs = require('fs');
const path = require('path');

const filePath = path.join(__dirname, 'output.txt');

// 写入文件(覆盖)
fs.writeFile(filePath, 'Hello Node.js\n', 'utf8', (err) => {
  if (err) return console.error('写入失败:', err.message);
  console.log('写入成功');
});

// 追加内容
fs.appendFile(filePath, '追加一行\n', 'utf8', (err) => {
  if (err) return console.error('追加失败:', err.message);
  console.log('追加成功');
});

// 同步写入
fs.writeFileSync(filePath, '同步写入内容\n', 'utf8');

// Promise 方式
const { writeFile, appendFile } = require('fs').promises;

async function writeDemo() {
  await writeFile(filePath, '覆盖内容\n', 'utf8');
  await appendFile(filePath, '追加内容\n', 'utf8');
  console.log('Promise写入完成');
}
writeDemo().catch(console.error);
文件信息
js 复制代码
// core/fs/file-stat.js
const fs = require('fs');
const path = require('path');

const filePath = path.join(__dirname, 'demo.txt');

fs.stat(filePath, (err, stats) => {
  if (err) return console.error(err.message);

  console.log('是否文件:', stats.isFile());
  console.log('是否目录:', stats.isDirectory());
  console.log('文件大小(字节):', stats.size);
  console.log('创建时间:', stats.birthtime.toLocaleString());
  console.log('修改时间:', stats.mtime.toLocaleString());
});

// 检查文件/目录是否存在
fs.access(filePath, fs.constants.F_OK, (err) => {
  console.log('文件存在:', !err);
});
目录操作
js 复制代码
// core/fs/dir-ops.js
const fs = require('fs');
const path = require('path');

const dirPath = path.join(__dirname, 'testdir');

// 创建目录
fs.mkdir(dirPath, { recursive: true }, (err) => {
  if (err) return console.error('创建目录失败:', err.message);
  console.log('目录创建成功');
});

// 读取目录内容
fs.readdir(__dirname, (err, files) => {
  if (err) return console.error(err.message);
  console.log('目录内容:', files);
});

// 读取目录内容(带文件类型)
fs.readdir(__dirname, { withFileTypes: true }, (err, entries) => {
  if (err) return console.error(err.message);
  entries.forEach((entry) => {
    const type = entry.isDirectory() ? '[目录]' : '[文件]';
    console.log(type, entry.name);
  });
});

// 删除目录(Node.js 14.14+)
fs.rm(dirPath, { recursive: true, force: true }, (err) => {
  if (err) return console.error(err.message);
  console.log('目录删除成功');
});
文件流

流(Stream)用于处理大文件,避免一次性读入内存。

js 复制代码
// core/fs/file-stream.js
const fs = require('fs');
const path = require('path');

const src = path.join(__dirname, 'large.txt');
const dest = path.join(__dirname, 'copy.txt');

// 可读流
const readable = fs.createReadStream(src, { encoding: 'utf8', highWaterMark: 64 * 1024 });

readable.on('data', (chunk) => {
  process.stdout.write(`收到 ${chunk.length} 字节\n`);
});
readable.on('end', () => console.log('读取完毕'));
readable.on('error', (err) => console.error('读取错误:', err.message));

// 可写流
const writable = fs.createWriteStream(dest);
writable.write('第一行\n');
writable.write('第二行\n');
writable.end('写入结束\n');
writable.on('finish', () => console.log('写入完毕'));

// 管道:直接从可读流复制到可写流(推荐用于文件复制)
fs.createReadStream(src)
  .pipe(fs.createWriteStream(dest))
  .on('finish', () => console.log('文件复制完成'));

2.2 路径处理 path

js 复制代码
// core/path/path-demo.js
const path = require('path');

// path.join:拼接路径(自动处理分隔符)
console.log(path.join('user', 'data', 'file.txt'));   // user/data/file.txt
console.log(path.join('/user', '../data', 'file.txt')); // /data/file.txt
// 注意:join 不会将相对路径转为绝对路径

// path.resolve:解析为绝对路径(从右向左,遇到 / 停止)
console.log(path.resolve('src', 'index.js'));          // /当前工作目录/src/index.js
console.log(path.resolve('/base', 'src', 'index.js')); // /base/src/index.js
console.log(path.resolve('/base', '/abs', 'file.js')); // /abs/file.js(遇到/开头停止)

// join vs resolve 区别
// join:纯字符串拼接,不关心当前目录
// resolve:从 process.cwd() 出发,等同于 cd 命令逐步进入

// path.dirname:获取目录部分
console.log(path.dirname('/user/data/file.txt'));      // /user/data

// path.basename:获取文件名
console.log(path.basename('/user/data/file.txt'));     // file.txt
console.log(path.basename('/user/data/file.txt', '.txt')); // file(去掉扩展名)

// path.extname:获取扩展名
console.log(path.extname('index.html'));               // .html
console.log(path.extname('archive.tar.gz'));           // .gz

// path.parse:解析路径为对象
const parsed = path.parse('/user/data/file.txt');
console.log(parsed);
// { root: '/', dir: '/user/data', base: 'file.txt', ext: '.txt', name: 'file' }

// path.format:对象转路径字符串(parse 的反操作)
console.log(path.format({ dir: '/user/data', name: 'file', ext: '.txt' }));
// /user/data/file.txt

// path.isAbsolute:判断是否绝对路径
console.log(path.isAbsolute('/user/data'));  // true
console.log(path.isAbsolute('src/index'));   // false

// path.relative:计算相对路径
console.log(path.relative('/data/from', '/data/to/file.txt')); // ../to/file.txt

// 跨平台路径分隔符
console.log(path.sep);    // POSIX: /   Windows: \
console.log(path.delimiter); // POSIX: :   Windows: ;

2.3 http 模块

创建 HTTP 服务器
js 复制代码
// core/http/basic-server.js
const http = require('http');

const server = http.createServer((req, res) => {
  // req: IncomingMessage(请求对象)
  // res: ServerResponse(响应对象)
  res.writeHead(200, { 'Content-Type': 'text/plain; charset=utf-8' });
  res.end('Hello Node.js HTTP Server!');
});

server.listen(3000, () => {
  console.log('服务器运行在 http://localhost:3000');
});
请求与响应
js 复制代码
// core/http/req-res.js
const http = require('http');

const server = http.createServer((req, res) => {
  // 请求信息
  console.log('方法:', req.method);      // GET POST PUT ...
  console.log('路径:', req.url);         // /api/users?page=1
  console.log('请求头:', req.headers);

  // 获取 POST 请求体
  if (req.method === 'POST') {
    let body = '';
    req.on('data', (chunk) => { body += chunk; });
    req.on('end', () => {
      console.log('请求体:', body);
      const data = JSON.parse(body);

      res.writeHead(200, { 'Content-Type': 'application/json' });
      res.end(JSON.stringify({ received: data, ok: true }));
    });
    return;
  }

  // 响应状态码和头
  res.statusCode = 200;
  res.setHeader('Content-Type', 'application/json');
  res.setHeader('X-Powered-By', 'Node.js');
  res.end(JSON.stringify({ message: 'ok' }));
});

server.listen(3000, () => console.log('http://localhost:3000'));
路由处理
js 复制代码
// core/http/router.js
const http = require('http');
const { URL } = require('url');

const BASE = 'http://localhost:3000';

function router(req, res) {
  const { pathname, searchParams } = new URL(req.url, BASE);
  const method = req.method;

  res.setHeader('Content-Type', 'application/json');

  if (method === 'GET' && pathname === '/') {
    res.end(JSON.stringify({ page: 'home' }));

  } else if (method === 'GET' && pathname === '/users') {
    const page = searchParams.get('page') || 1;
    res.end(JSON.stringify({ users: [], page: Number(page) }));

  } else if (method === 'GET' && pathname.startsWith('/users/')) {
    const id = pathname.split('/')[2];
    res.end(JSON.stringify({ user: { id } }));

  } else {
    res.writeHead(404);
    res.end(JSON.stringify({ error: 'Not Found' }));
  }
}

http.createServer(router).listen(3000, () => {
  console.log('路由服务器运行在 http://localhost:3000');
  console.log('  GET /          首页');
  console.log('  GET /users     用户列表');
  console.log('  GET /users/1   用户详情');
});
静态文件服务
js 复制代码
// core/http/static-server.js
const http = require('http');
const fs = require('fs');
const path = require('path');

const STATIC_DIR = path.join(__dirname, 'public');

const MIME_TYPES = {
  '.html': 'text/html; charset=utf-8',
  '.css':  'text/css',
  '.js':   'application/javascript',
  '.json': 'application/json',
  '.png':  'image/png',
  '.jpg':  'image/jpeg',
  '.svg':  'image/svg+xml',
  '.ico':  'image/x-icon',
};

http.createServer((req, res) => {
  // 防止路径穿越攻击
  const safePath = path.normalize(req.url).replace(/^(\.\.[/\\])+/, '');
  const filePath = path.join(STATIC_DIR, safePath === '/' ? 'index.html' : safePath);
  const ext = path.extname(filePath);
  const contentType = MIME_TYPES[ext] || 'application/octet-stream';

  fs.readFile(filePath, (err, data) => {
    if (err) {
      res.writeHead(404, { 'Content-Type': 'text/plain' });
      res.end('404 Not Found');
      return;
    }
    res.writeHead(200, { 'Content-Type': contentType });
    res.end(data);
  });
}).listen(3000, () => {
  console.log('静态文件服务 http://localhost:3000');
  console.log('根目录:', STATIC_DIR);
});

2.4 事件 events

js 复制代码
// core/events/events-demo.js
const EventEmitter = require('events');

class TaskQueue extends EventEmitter {
  constructor() {
    super();
    this.tasks = [];
  }

  add(task) {
    this.tasks.push(task);
    this.emit('task:added', task);
  }

  run() {
    if (this.tasks.length === 0) {
      this.emit('empty');
      return;
    }
    const task = this.tasks.shift();
    this.emit('task:start', task);
    // 模拟异步执行
    setTimeout(() => {
      this.emit('task:done', task);
      this.run(); // 继续处理下一个
    }, 100);
  }
}

const queue = new TaskQueue();

// on:持续监听
queue.on('task:added', (task) => console.log('新增任务:', task));
queue.on('task:start', (task) => console.log('开始执行:', task));
queue.on('task:done', (task) => console.log('任务完成:', task));

// once:只触发一次
queue.once('empty', () => console.log('队列清空'));

queue.add('task-A');
queue.add('task-B');
queue.add('task-C');
queue.run();

// 移除监听器
function onDone(task) { console.log('外部监听 done:', task); }
queue.on('task:done', onDone);
queue.off('task:done', onDone); // removeListener 的别名

// 查看监听器数量
console.log('task:done 监听器数:', queue.listenerCount('task:done'));

// 最大监听器数量(超出会警告,默认10)
queue.setMaxListeners(20);

// 错误事件(必须监听,否则会抛出异常)
queue.on('error', (err) => console.error('错误:', err.message));

2.5 流 Stream

可读流
js 复制代码
// core/stream/readable.js
const { Readable } = require('stream');

// 自定义可读流
class NumberStream extends Readable {
  constructor(max) {
    super({ objectMode: false });
    this.current = 1;
    this.max = max;
  }

  _read() {
    if (this.current <= this.max) {
      this.push(`number: ${this.current}\n`);
      this.current++;
    } else {
      this.push(null); // null 表示流结束
    }
  }
}

const stream = new NumberStream(5);

// 方式1:data 事件
stream.on('data', (chunk) => process.stdout.write(chunk));
stream.on('end', () => console.log('流结束'));

// 方式2:for await(推荐,Node.js 10+)
async function readStream() {
  const s = new NumberStream(5);
  for await (const chunk of s) {
    process.stdout.write(chunk);
  }
  console.log('流结束');
}
readStream();
可写流
js 复制代码
// core/stream/writable.js
const { Writable } = require('stream');

// 自定义可写流(写入内存)
class MemoryWritable extends Writable {
  constructor() {
    super();
    this.buffer = [];
  }

  _write(chunk, encoding, callback) {
    this.buffer.push(chunk.toString());
    callback(); // 必须调用,告知可以继续写入
  }

  getContent() {
    return this.buffer.join('');
  }
}

const writer = new MemoryWritable();

writer.write('第一段内容\n');
writer.write('第二段内容\n');
writer.end('结束\n');

writer.on('finish', () => {
  console.log('写入完成,内容:');
  console.log(writer.getContent());
});
管道 pipe
js 复制代码
// core/stream/pipe-demo.js
const fs = require('fs');
const zlib = require('zlib');
const path = require('path');

const src  = path.join(__dirname, 'input.txt');
const dest = path.join(__dirname, 'input.txt.gz');

// pipe:将可读流连接到可写流
// 读取文件 → gzip 压缩 → 写入文件
fs.createReadStream(src)
  .pipe(zlib.createGzip())
  .pipe(fs.createWriteStream(dest))
  .on('finish', () => console.log('压缩完成:', dest));

// pipeline(推荐,自动处理错误和清理)
const { pipeline } = require('stream');

pipeline(
  fs.createReadStream(src),
  zlib.createGzip(),
  fs.createWriteStream(dest),
  (err) => {
    if (err) console.error('管道错误:', err.message);
    else console.log('pipeline 压缩完成');
  }
);

2.6 Buffer

Buffer 用于处理二进制数据(图片、文件、网络数据等),是固定大小的内存块。

js 复制代码
// core/buffer/buffer-demo.js

// 创建 Buffer
const buf1 = Buffer.alloc(10);             // 10字节,全零
const buf2 = Buffer.alloc(10, 0xff);       // 10字节,填充0xff
const buf3 = Buffer.from('Hello Node.js'); // 从字符串创建(UTF-8)
const buf4 = Buffer.from([0x48, 0x65, 0x6c, 0x6c, 0x6f]); // 从字节数组

console.log('buf3 长度:', buf3.length);       // 13
console.log('buf3 内容:', buf3.toString());   // Hello Node.js
console.log('buf3 十六进制:', buf3.toString('hex'));  // 48656c6c6f...
console.log('buf3 base64:', buf3.toString('base64')); // SGVsbG8gTm9kZS5qcw==

// 读写 Buffer
const buf = Buffer.alloc(4);
buf.writeUInt8(255, 0);      // 在偏移0写入无符号8位整数
buf.writeUInt16BE(1000, 1);  // 在偏移1写入大端序16位整数
buf.writeUInt8(42, 3);
console.log('buf:', buf);

// 切片(与原 Buffer 共享内存)
const slice = buf3.subarray(0, 5);
console.log('切片:', slice.toString()); // Hello

// 合并 Buffer
const combined = Buffer.concat([buf3, Buffer.from(' World!')]);
console.log('合并:', combined.toString()); // Hello Node.js World!

// 比较
console.log('相等:', buf3.equals(Buffer.from('Hello Node.js'))); // true

// Buffer 与字符串互转
const str = 'Node.js 中文';
const encoded = Buffer.from(str, 'utf8');
const decoded = encoded.toString('utf8');
console.log('编码字节数:', encoded.length); // 中文3字节/字符
console.log('解码:', decoded);

// Buffer 转 JSON
console.log(JSON.stringify(Buffer.from('abc')));
// {"type":"Buffer","data":[97,98,99]}

2.7 其他核心模块

url
js 复制代码
// core/others/url-demo.js
const { URL, URLSearchParams } = require('url');

const myUrl = new URL('https://user:pass@example.com:8080/api/users?page=1&limit=10#top');

console.log('href:    ', myUrl.href);
console.log('protocol:', myUrl.protocol); // https:
console.log('username:', myUrl.username); // user
console.log('host:    ', myUrl.host);     // example.com:8080
console.log('hostname:', myUrl.hostname); // example.com
console.log('port:    ', myUrl.port);     // 8080
console.log('pathname:', myUrl.pathname); // /api/users
console.log('search:  ', myUrl.search);  // ?page=1&limit=10
console.log('hash:    ', myUrl.hash);    // #top

// 操作查询参数
myUrl.searchParams.set('page', '2');
myUrl.searchParams.append('sort', 'name');
myUrl.searchParams.delete('limit');
console.log('新 URL:', myUrl.toString());

// URLSearchParams 单独使用
const params = new URLSearchParams('a=1&b=2&b=3');
console.log('a:', params.get('a'));      // 1
console.log('b all:', params.getAll('b')); // ['2','3']
params.forEach((val, key) => console.log(key, '->', val));
querystring(旧版,建议用 URLSearchParams)
js 复制代码
// core/others/querystring-demo.js
const qs = require('querystring');

// 解析查询字符串
const parsed = qs.parse('name=node&version=20&tags=js&tags=server');
console.log(parsed);
// { name: 'node', version: '20', tags: ['js', 'server'] }

// 序列化对象为查询字符串
const str = qs.stringify({ name: 'node', version: 20, tags: ['js', 'server'] });
console.log(str); // name=node&version=20&tags=js&tags=server
crypto

crypto 是 Node.js 内置的加密模块,提供哈希、HMAC、对称加密/解密、随机数生成等功能,底层基于 OpenSSL 实现。

常用功能:

功能 方法 说明
哈希摘要 createHash 单向不可逆,用于完整性校验
消息认证码 createHmac 带密钥的哈希,单向不可逆
对称加密 createCipheriv 可逆,需配合 createDecipheriv 解密
随机字节 randomBytes 生成安全随机数
js 复制代码
// core/others/crypto-demo.js
const crypto = require('crypto');

// MD5 哈希(不推荐用于密码,仅用于校验)
const md5 = crypto.createHash('md5').update('hello').digest('hex');
console.log('MD5:', md5);

// SHA-256 哈希
const sha256 = crypto.createHash('sha256').update('hello').digest('hex');
console.log('SHA256:', sha256);

// HMAC(消息认证码,需要密钥)
const hmac = crypto.createHmac('sha256', 'secret-key').update('hello').digest('hex');
console.log('HMAC:', hmac);

// 随机字节(用于生成 token)
const token = crypto.randomBytes(32).toString('hex');
console.log('随机token:', token);

// 对称加密 AES-256-CBC
const ALGORITHM = 'aes-256-cbc';
const KEY = crypto.randomBytes(32);  // 32字节密钥
const IV  = crypto.randomBytes(16);  // 16字节初始向量

function encrypt(text) {
  // 创建加密器,使用相同的算法、密钥和初始向量
  const cipher = crypto.createCipheriv(ALGORITHM, KEY, IV);
  // update() 处理明文,final() 处理剩余填充块,concat 合并后转十六进制字符串
  return Buffer.concat([cipher.update(text, 'utf8'), cipher.final()]).toString('hex');
}

function decrypt(encrypted) {
  // 创建解密器,必须使用与加密时完全相同的算法、密钥和初始向量
  const decipher = crypto.createDecipheriv(ALGORITHM, KEY, IV);
  // 将十六进制密文还原为 Buffer,update() + final() 完成解密并转为字符串
  return Buffer.concat([
    decipher.update(Buffer.from(encrypted, 'hex')),
    decipher.final()
  ]).toString('utf8');
}

const plaintext  = 'Node.js 加密示例';
const ciphertext = encrypt(plaintext);
const decrypted  = decrypt(ciphertext);
console.log('原文:', plaintext);
console.log('密文:', ciphertext);
console.log('解密:', decrypted);
util
js 复制代码
// core/others/util-demo.js
const util = require('util');
const fs = require('fs');

// util.promisify:将 callback 风格函数转为 Promise
const readFile = util.promisify(fs.readFile);

async function demo() {
  const data = await readFile(__filename, 'utf8');
  console.log('文件行数:', data.split('\n').length);
}
demo();

// util.format:格式化字符串
console.log(util.format('Hello %s, you are %d years old', 'Node', 20));
// Hello Node, you are 20 years old
console.log(util.format('Object: %j', { a: 1, b: 2 }));
// Object: {"a":1,"b":2}

// util.inspect:将对象转为可读字符串
// 比 JSON.stringify 更强:支持循环引用、函数、Symbol、Map/Set、自定义颜色、控制嵌套深度
const obj = { a: 1, b: [1, 2, 3], c: { d: true } };
console.log(util.inspect(obj, { depth: null, colors: true }));

// util.isDeepStrictEqual:深度严格相等比较
console.log(util.isDeepStrictEqual({ a: 1 }, { a: 1 })); // true
console.log(util.isDeepStrictEqual({ a: 1 }, { a: '1' })); // false(严格模式)

// util.deprecate:标记函数为废弃
const oldFn = util.deprecate(
  () => console.log('旧函数'),
  '请使用 newFn 代替'
);
oldFn(); // 调用时会打印废弃警告

3. Web 框架

3.1 Express 框架

Express 是 Node.js 最流行的 Web 框架,轻量、灵活,提供路由、中间件等核心能力。

安装和使用
bash 复制代码
npm install express
js 复制代码
// express/basic.js
const express = require('express');
const app = express();

// 解析 JSON 请求体
app.use(express.json());
// 解析 URL 编码请求体(表单)
app.use(express.urlencoded({ extended: true }));

app.get('/', (req, res) => {
  res.send('Hello Express!');
});

app.listen(3000, () => {
  console.log('Express 服务启动: http://localhost:3000');
});
路由
js 复制代码
// express/router.js
const express = require('express');
const router = express.Router();

// GET 列表
router.get('/users', (req, res) => {
  const { page = 1, limit = 10 } = req.query;
  res.json({ users: [], page: Number(page), limit: Number(limit) });
});

// GET 详情(路由参数)
router.get('/users/:id', (req, res) => {
  const { id } = req.params;
  res.json({ user: { id } });
});

// POST 创建
router.post('/users', (req, res) => {
  const { name, email } = req.body;
  res.status(201).json({ user: { id: Date.now(), name, email } });
});

// PUT 更新
router.put('/users/:id', (req, res) => {
  const { id } = req.params;
  const { name, email } = req.body;
  res.json({ user: { id, name, email } });
});

// DELETE 删除
router.delete('/users/:id', (req, res) => {
  const { id } = req.params;
  res.json({ deleted: id });
});

module.exports = router;
js 复制代码
// express/app.js 中挂载路由
const express = require('express');
const userRouter = require('./router');

const app = express();
app.use(express.json());
app.use('/api', userRouter);

app.listen(3000);
中间件

中间件是一个函数 (req, res, next) => {},可以拦截、处理请求,或将控制权传递给下一个中间件。

js 复制代码
// express/middleware.js
const express = require('express');
const app = express();

// 应用级中间件:每个请求都执行
app.use((req, res, next) => {
  console.log(`[${new Date().toISOString()}] ${req.method} ${req.url}`);
  next(); // 必须调用,否则请求挂起
});

// 路由级中间件:只对指定路由生效
function authMiddleware(req, res, next) {
  const token = req.headers['authorization'];
  if (!token) {
    return res.status(401).json({ error: '未授权' });
  }
  next();
}

app.get('/profile', authMiddleware, (req, res) => {
  res.json({ user: 'admin' });
});

// 第三方中间件示例
// npm install cors morgan
const cors = require('cors');
const morgan = require('morgan');
app.use(cors());           // 跨域处理
app.use(morgan('dev'));    // 请求日志

app.listen(3000);
请求与响应
js 复制代码
// express/req-res.js
const express = require('express');
const app = express();
app.use(express.json());

app.post('/demo', (req, res) => {
  // 请求
  console.log(req.params);    // 路由参数 /users/:id
  console.log(req.query);     // 查询参数 ?page=1
  console.log(req.body);      // 请求体(需中间件解析)
  console.log(req.headers);   // 请求头
  console.log(req.ip);        // 客户端 IP

  // 响应
  res.status(200)             // 设置状态码
    .set('X-Custom', 'value') // 设置响应头
    .json({ ok: true });      // 发送 JSON(自动设置 Content-Type)

  // 其他响应方式
  // res.send('文本')          // 文本/HTML
  // res.sendFile('/path/to/file')  // 发送文件
  // res.redirect('/other')   // 重定向
  // res.download('/file')    // 触发下载
});

app.listen(3000);
错误处理
js 复制代码
// express/error-handler.js
const express = require('express');
const app = express();
app.use(express.json());

// 自定义错误类
class AppError extends Error {
  constructor(message, statusCode) {
    super(message);
    this.statusCode = statusCode;
  }
}

app.get('/users/:id', (req, res, next) => {
  const { id } = req.params;
  if (id === '0') {
    return next(new AppError('用户不存在', 404));
  }
  res.json({ user: { id } });
});

// 异步路由的错误捕获(推荐封装 asyncHandler)
function asyncHandler(fn) {
  return (req, res, next) => Promise.resolve(fn(req, res, next)).catch(next);
}

app.get('/async', asyncHandler(async (req, res) => {
  // 异步操作,错误自动传递给错误处理中间件
  throw new AppError('演示错误', 500);
}));

// 错误处理中间件(4个参数,必须放最后)
app.use((err, req, res, next) => {
  const statusCode = err.statusCode || 500;
  res.status(statusCode).json({
    error: err.message || '服务器内部错误',
  });
});

app.listen(3000);

3.2 Koa 框架

Koa 由 Express 原班人马打造,更现代、更精简,核心只有中间件机制,无内置路由。

Koa 特点
特性 Express Koa
中间件机制 线性(next 传递) 洋葱模型(async/await)
内置路由 ❌(需 koa-router)
错误处理 4 参数中间件 try/catch + ctx.status
请求/响应 req / res ctx.request / ctx.response
异步支持 callback / Promise 原生 async/await
bash 复制代码
npm install koa koa-router koa-bodyparser
Koa 中间件(洋葱模型)
js 复制代码
// koa/middleware.js
const Koa = require('koa');
const app = new Koa();

// 洋葱模型:请求进入时从外到内,响应时从内到外
app.use(async (ctx, next) => {
  console.log('中间件1 进入');
  await next();             // 等待内层中间件执行完
  console.log('中间件1 返回');
});

app.use(async (ctx, next) => {
  console.log('中间件2 进入');
  await next();
  console.log('中间件2 返回');
});

app.use(async (ctx) => {
  console.log('处理请求');
  ctx.body = { ok: true };
});

// 输出顺序:中间件1进入 → 中间件2进入 → 处理请求 → 中间件2返回 → 中间件1返回
app.listen(3000);
Koa 路由
js 复制代码
// koa/app.js
const Koa = require('koa');
const Router = require('koa-router');
const bodyParser = require('koa-bodyparser');

const app = new Koa();
const router = new Router();

app.use(bodyParser());

// 错误处理(放最外层)
app.use(async (ctx, next) => {
  try {
    await next();
  } catch (err) {
    ctx.status = err.status || 500;
    ctx.body = { error: err.message };
  }
});

router.get('/users', async (ctx) => {
  const { page = 1 } = ctx.query;
  ctx.body = { users: [], page: Number(page) };
});

router.get('/users/:id', async (ctx) => {
  const { id } = ctx.params;
  ctx.body = { user: { id } };
});

router.post('/users', async (ctx) => {
  const { name, email } = ctx.request.body;
  ctx.status = 201;
  ctx.body = { user: { id: Date.now(), name, email } };
});

app.use(router.routes());
app.use(router.allowedMethods());

app.listen(3000, () => console.log('Koa 服务启动: http://localhost:3000'));

3.3 RESTful API 设计

HTTP 方法
方法 语义 示例
GET 获取资源 GET /users / GET /users/1
POST 创建资源 POST /users
PUT 全量更新 PUT /users/1
PATCH 部分更新 PATCH /users/1
DELETE 删除资源 DELETE /users/1
状态码
状态码 含义 场景
200 OK 请求成功
201 Created 资源创建成功
204 No Content 删除成功(无响应体)
400 Bad Request 参数错误
401 Unauthorized 未登录/Token 无效
403 Forbidden 无权限
404 Not Found 资源不存在
409 Conflict 资源冲突(如重复创建)
422 Unprocessable Entity 数据验证失败
500 Internal Server Error 服务器内部错误
参数传递
js 复制代码
// restful/param-demo.js
const express = require('express');
const app = express();
app.use(express.json());

// 1. 路由参数(资源 ID):/users/:id
app.get('/users/:id', (req, res) => {
  const { id } = req.params;      // "123"
  res.json({ id });
});

// 2. 查询参数(过滤/分页/排序):/users?page=1&sort=name
app.get('/users', (req, res) => {
  const { page = 1, limit = 10, sort = 'id' } = req.query;
  res.json({ page: Number(page), limit: Number(limit), sort });
});

// 3. 请求体(创建/更新数据)
app.post('/users', (req, res) => {
  const { name, email } = req.body;
  res.status(201).json({ user: { name, email } });
});

// 4. 请求头(认证、版本等)
app.get('/profile', (req, res) => {
  const token = req.headers['authorization']; // "Bearer xxx"
  const version = req.headers['api-version']; // "v2"
  res.json({ token, version });
});

app.listen(3000);
API 文档(JSDoc 注释风格)
js 复制代码
// restful/api-docs.js
/**
 * @route   GET /api/users
 * @desc    获取用户列表
 * @access  Public
 * @query   {number} page - 页码,默认 1
 * @query   {number} limit - 每页条数,默认 10
 * @returns {object} { users: User[], total: number, page: number }
 */

/**
 * @route   POST /api/users
 * @desc    创建用户
 * @access  Private(需 Bearer Token)
 * @body    {string} name - 用户名(必填)
 * @body    {string} email - 邮箱(必填)
 * @returns {object} { user: User }
 * @error   400 - 参数缺失
 * @error   409 - 邮箱已存在
 */

3.4 身份认证

bash 复制代码
npm install express-session
js 复制代码
// auth/session-demo.js
const express = require('express');
const session = require('express-session');

const app = express();
app.use(express.json());
app.use(session({
  secret: 'my-secret-key',   // 签名密钥
  resave: false,
  saveUninitialized: false,
  cookie: {
    maxAge: 1000 * 60 * 60,  // 1小时
    httpOnly: true,           // 禁止 JS 访问
    secure: false,            // 生产环境设为 true(HTTPS)
  }
}));

// 登录
app.post('/login', (req, res) => {
  const { username, password } = req.body;
  if (username === 'admin' && password === '123456') {
    req.session.user = { username, role: 'admin' };
    return res.json({ ok: true });
  }
  res.status(401).json({ error: '用户名或密码错误' });
});

// 受保护路由
app.get('/profile', (req, res) => {
  if (!req.session.user) {
    return res.status(401).json({ error: '请先登录' });
  }
  res.json({ user: req.session.user });
});

// 退出
app.post('/logout', (req, res) => {
  req.session.destroy();
  res.json({ ok: true });
});

app.listen(3000);
JWT

JWT(JSON Web Token):无状态认证,适合前后端分离。

css 复制代码
Header.Payload.Signature
bash 复制代码
npm install jsonwebtoken
js 复制代码
// auth/jwt-demo.js
const express = require('express');
const jwt = require('jsonwebtoken');

const app = express();
app.use(express.json());

const JWT_SECRET = 'my-jwt-secret';
const JWT_EXPIRES = '7d';

// 登录 → 签发 Token
app.post('/login', (req, res) => {
  const { username, password } = req.body;
  if (username === 'admin' && password === '123456') {
    const token = jwt.sign(
      { id: 1, username, role: 'admin' }, // payload
      JWT_SECRET,
      { expiresIn: JWT_EXPIRES }
    );
    return res.json({ token });
  }
  res.status(401).json({ error: '用户名或密码错误' });
});

// JWT 验证中间件
function jwtAuth(req, res, next) {
  const auth = req.headers['authorization'];
  if (!auth || !auth.startsWith('Bearer ')) {
    return res.status(401).json({ error: '缺少 Token' });
  }
  try {
    const payload = jwt.verify(auth.slice(7), JWT_SECRET);
    req.user = payload;
    next();
  } catch (err) {
    res.status(401).json({ error: 'Token 无效或已过期' });
  }
}

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

app.listen(3000);
OAuth 2.0(概念说明)

OAuth 2.0 是一个授权框架,允许第三方应用获取用户资源的有限访问权限,常见于"使用 GitHub / Google 登录"场景。

核心流程(Authorization Code Flow):

css 复制代码
用户 → 客户端 → 授权服务器(GitHub)→ 返回 code
客户端 → 用 code 换 access_token
客户端 → 用 access_token 访问资源服务器(获取用户信息)

四种授权模式:

模式 适用场景
Authorization Code 服务端 Web 应用(最安全,推荐)
PKCE 单页应用(SPA)/ 移动端
Client Credentials 服务间调用(无用户参与)
Resource Owner Password 高度信任的第一方应用(不推荐)
bash 复制代码
# Passport.js 是 Node.js 最流行的 OAuth 库
npm install passport passport-github2

# example:  auth/auth2.0.js 
---

### 3.5 文件上传(Multer)

Multer 是 Express 的文件上传中间件,基于 `multipart/form-data`。

```bash
npm install multer
js 复制代码
// upload/upload-demo.js
const express = require('express');
const multer = require('multer');
const path = require('path');

const app = express();

// 配置存储
const storage = multer.diskStorage({
  destination(req, file, cb) {
    cb(null, 'uploads/');          // 存储目录(需提前创建)
  },
  filename(req, file, cb) {
    const ext = path.extname(file.originalname);
    const name = `${Date.now()}-${Math.random().toString(36).slice(2)}${ext}`;
    cb(null, name);
  }
});

// 文件类型过滤
function fileFilter(req, file, cb) {
  const allowed = ['.jpg', '.jpeg', '.png', '.gif', '.pdf'];
  const ext = path.extname(file.originalname).toLowerCase();
  if (allowed.includes(ext)) {
    cb(null, true);
  } else {
    cb(new Error('不支持的文件类型'), false);
  }
}

const upload = multer({
  storage,
  fileFilter,
  limits: { fileSize: 5 * 1024 * 1024 }  // 最大 5MB
});

// 单文件上传(字段名 file)
app.post('/upload/single', upload.single('file'), (req, res) => {
  if (!req.file) return res.status(400).json({ error: '请选择文件' });
  res.json({
    filename: req.file.filename,
    size: req.file.size,
    mimetype: req.file.mimetype,
    url: `/uploads/${req.file.filename}`
  });
});

// 多文件上传(最多 5 个)
app.post('/upload/multiple', upload.array('files', 5), (req, res) => {
  const files = req.files.map(f => ({
    filename: f.filename,
    size: f.size,
    url: `/uploads/${f.filename}`
  }));
  res.json({ files });
});

// 静态访问上传文件
app.use('/uploads', express.static('uploads'));

// 上传错误处理
app.use((err, req, res, next) => {
  if (err instanceof multer.MulterError) {
    return res.status(400).json({ error: `上传错误: ${err.message}` });
  }
  res.status(500).json({ error: err.message });
});

app.listen(3000, () => console.log('文件上传服务: http://localhost:3000'));

4. 数据库

4.1 MySQL 操作(mysql2)

安装与连接
bash 复制代码
npm install mysql2
js 复制代码
// db/mysql/connection.js
const mysql = require('mysql2/promise');

// 创建连接池(推荐,自动管理连接复用)
const pool = mysql.createPool({
  host: 'localhost',
  port: 3306,
  user: 'root',
  password: 'your_password',
  database: 'test_db',
  waitForConnections: true,
  connectionLimit: 10,     // 最大连接数
  queueLimit: 0
});

module.exports = pool;
执行 SQL
js 复制代码
// db/mysql/query-demo.js
const pool = require('./connection');

async function demo() {
  // 查询
  const [rows] = await pool.query('SELECT * FROM users WHERE id = ?', [1]);
  console.log('查询结果:', rows);

  // 插入
  const [result] = await pool.query(
    'INSERT INTO users (name, email) VALUES (?, ?)',
    ['张三', 'zhangsan@example.com']
  );
  console.log('新增 ID:', result.insertId);

  // 更新
  const [updateResult] = await pool.query(
    'UPDATE users SET name = ? WHERE id = ?',
    ['李四', 1]
  );
  console.log('影响行数:', updateResult.affectedRows);

  // 删除
  await pool.query('DELETE FROM users WHERE id = ?', [1]);
}

demo().catch(console.error);
参数化查询

参数化查询(Prepared Statement)将 SQL 与数据分离,防止 SQL 注入攻击

js 复制代码
// db/mysql/prepared.js
const pool = require('./connection');

// ❌ 危险:拼接字符串(SQL 注入漏洞)
// pool.query(`SELECT * FROM users WHERE name = '${userInput}'`);

// ✅ 安全:参数化查询,? 占位符由驱动处理转义
async function findUser(name) {
  const [rows] = await pool.query(
    'SELECT * FROM users WHERE name = ?',
    [name]
  );
  return rows;
}

// 批量插入
async function batchInsert(users) {
  const values = users.map(u => [u.name, u.email]);
  const [result] = await pool.query(
    'INSERT INTO users (name, email) VALUES ?',
    [values]
  );
  return result.affectedRows;
}
连接池
js 复制代码
// db/mysql/pool-demo.js
const pool = require('./connection');

// 手动获取连接(事务场景)
async function transfer(fromId, toId, amount) {
  const conn = await pool.getConnection();
  try {
    await conn.beginTransaction();

    await conn.query('UPDATE accounts SET balance = balance - ? WHERE id = ?', [amount, fromId]);
    await conn.query('UPDATE accounts SET balance = balance + ? WHERE id = ?', [amount, toId]);

    await conn.commit();
    console.log('转账成功');
  } catch (err) {
    await conn.rollback();
    console.error('转账失败,已回滚:', err.message);
    throw err;
  } finally {
    conn.release(); // 归还连接到池
  }
}

4.2 MongoDB(Mongoose)

Mongoose 是 MongoDB 的 ODM(对象文档映射)库,提供 Schema 约束和模型操作。

bash 复制代码
npm install mongoose
Schema / Model
js 复制代码
// db/mongo/models/User.js
const mongoose = require('mongoose');

// Schema:定义文档结构和约束
const userSchema = new mongoose.Schema({
  name:      { type: String, required: true, trim: true },
  email:     { type: String, required: true, unique: true, lowercase: true },
  age:       { type: Number, min: 0, max: 150 },
  role:      { type: String, enum: ['user', 'admin'], default: 'user' },
  createdAt: { type: Date, default: Date.now }
});

// Model:对应 MongoDB 中的 users 集合
const User = mongoose.model('User', userSchema);

module.exports = User;
连接数据库
js 复制代码
// db/mongo/connection.js
const mongoose = require('mongoose');

async function connect() {
  await mongoose.connect('mongodb://localhost:27017/test_db', {
    // Mongoose 6+ 无需再传 useNewUrlParser 等选项
  });
  console.log('MongoDB 连接成功');
}

// 监听连接事件
mongoose.connection.on('disconnected', () => console.warn('MongoDB 断开连接'));
mongoose.connection.on('error', (err) => console.error('MongoDB 错误:', err));

module.exports = { connect };
CRUD 操作
js 复制代码
// db/mongo/crud-demo.js
const mongoose = require('mongoose');
const User = require('./models/User');

async function demo() {
  await mongoose.connect('mongodb://localhost:27017/test_db');

  // 创建
  const user = await User.create({ name: '张三', email: 'zs@example.com', age: 25 });
  console.log('创建:', user._id);

  // 查询
  const users = await User.find({ role: 'user' }).select('name email').limit(10);
  const one   = await User.findById(user._id);
  const byEmail = await User.findOne({ email: 'zs@example.com' });

  // 更新
  await User.findByIdAndUpdate(user._id, { age: 26 }, { new: true }); // new: true 返回更新后文档

  // 删除
  await User.findByIdAndDelete(user._id);

  // 统计
  const count = await User.countDocuments({ role: 'user' });
  console.log('用户数:', count);

  await mongoose.disconnect();
}

demo().catch(console.error);

4.3 ORM 框架

ORM(对象关系映射)将数据库表映射为 JavaScript 对象,避免手写 SQL。

Sequelize

适合 MySQL / PostgreSQL / SQLite,历史悠久、社区成熟。

bash 复制代码
npm install sequelize mysql2
js 复制代码
// db/orm/sequelize-demo.js
const { Sequelize, DataTypes } = require('sequelize');

const sequelize = new Sequelize('test_db', 'root', 'password', {
  host: 'localhost',
  dialect: 'mysql',
  logging: false,
});

// 定义模型
const User = sequelize.define('User', {
  name:  { type: DataTypes.STRING, allowNull: false },
  email: { type: DataTypes.STRING, unique: true, allowNull: false },
  age:   { type: DataTypes.INTEGER },
}, { tableName: 'users' });

async function demo() {
  await sequelize.authenticate();    // 测试连接
  await sequelize.sync({ alter: true }); // 同步表结构

  const user = await User.create({ name: '张三', email: 'zs@example.com', age: 25 });
  const found = await User.findOne({ where: { email: 'zs@example.com' } });
  await User.update({ age: 26 }, { where: { id: user.id } });
  await User.destroy({ where: { id: user.id } });
}

demo().catch(console.error);
Prisma

现代 ORM,类型安全,自动生成类型定义,适合 TypeScript 项目。

bash 复制代码
npm install prisma @prisma/client
npx prisma init
prisma 复制代码
// prisma/schema.prisma
datasource db {
  provider = "mysql"
  url      = env("DATABASE_URL")
}

generator client {
  provider = "prisma-client-js"
}

model User {
  id        Int      @id @default(autoincrement())
  name      String
  email     String   @unique
  age       Int?
  createdAt DateTime @default(now())
}
js 复制代码
// db/orm/prisma-demo.js
const { PrismaClient } = require('@prisma/client');
const prisma = new PrismaClient();

async function demo() {
  const user  = await prisma.user.create({ data: { name: '张三', email: 'zs@example.com' } });
  const users = await prisma.user.findMany({ where: { age: { gte: 18 } } });
  await prisma.user.update({ where: { id: user.id }, data: { age: 26 } });
  await prisma.user.delete({ where: { id: user.id } });

  await prisma.$disconnect();
}

demo().catch(console.error);
TypeORM

支持 TypeScript 装饰器,适合面向对象风格。

bash 复制代码
npm install typeorm mysql2 reflect-metadata
js 复制代码
// db/orm/typeorm-demo.js
const { DataSource, Entity, PrimaryGeneratedColumn, Column } = require('typeorm');

// 定义实体
@Entity('users')
class User {
  @PrimaryGeneratedColumn()
  id;

  @Column()
  name;

  @Column({ unique: true })
  email;
}

const AppDataSource = new DataSource({
  type: 'mysql',
  host: 'localhost',
  port: 3306,
  username: 'root',
  password: 'password',
  database: 'test_db',
  entities: [User],
  synchronize: true,  // 自动同步表结构(生产环境关闭)
});

async function demo() {
  await AppDataSource.initialize();
  const repo = AppDataSource.getRepository(User);

  const user  = repo.create({ name: '张三', email: 'zs@example.com' });
  await repo.save(user);
  const found = await repo.findOneBy({ email: 'zs@example.com' });
  await repo.remove(found);

  await AppDataSource.destroy();
}

demo().catch(console.error);

三者对比:

特性 Sequelize Prisma TypeORM
数据库支持 MySQL/PG/SQLite/MSSQL MySQL/PG/SQLite/MongoDB MySQL/PG/SQLite/MongoDB/MSSQL
TypeScript 一般 最优(自动生成类型) 好(装饰器)
学习成本
迁移管理 手动 prisma migrate typeorm migration
适合场景 传统 Node 项目 现代 TS 全栈 企业级 OOP 项目

4.4 Redis 操作

Redis 是基于内存的键值存储,常用于缓存、会话、消息队列等场景。

bash 复制代码
npm install redis
js 复制代码
// db/redis/redis-demo.js
const { createClient } = require('redis');

const client = createClient({
  url: 'redis://localhost:6379'
  // password: 'your_password'  // 有密码时配置
});

client.on('error', (err) => console.error('Redis 错误:', err));

async function demo() {
  await client.connect();

  // 字符串:缓存操作
  await client.set('name', 'Node.js');
  await client.set('counter', '0');
  await client.expire('name', 60);           // 60 秒过期
  const name = await client.get('name');
  console.log('name:', name);                // Node.js

  // 自增(计数器)
  await client.incr('counter');
  await client.incrBy('counter', 5);

  // 哈希:存储对象
  await client.hSet('user:1', { name: '张三', age: '25', role: 'admin' });
  const user = await client.hGetAll('user:1');
  console.log('user:', user);

  // 列表:消息队列
  await client.lPush('queue', 'task1', 'task2');
  const task = await client.rPop('queue');   // 从右侧取出
  console.log('task:', task);

  // Set:去重集合
  await client.sAdd('tags', 'node', 'js', 'node'); // 自动去重
  const tags = await client.sMembers('tags');
  console.log('tags:', tags);

  // 检查 key 是否存在
  const exists = await client.exists('name');
  console.log('exists:', exists); // 1

  // 删除 key
  await client.del('name');

  await client.disconnect();
}

demo().catch(console.error);
缓存实战(Express + Redis)
js 复制代码
// db/redis/cache-middleware.js
const { createClient } = require('redis');

const redis = createClient({ url: 'redis://localhost:6379' });
redis.connect();

// 缓存中间件(GET 请求缓存 60 秒)
function cache(ttl = 60) {
  return async (req, res, next) => {
    const key = `cache:${req.originalUrl}`;
    const cached = await redis.get(key);
    if (cached) {
      return res.json(JSON.parse(cached)); // 命中缓存
    }
    // 劫持 res.json,写入缓存后再响应
    const originJson = res.json.bind(res);
    res.json = async (data) => {
      await redis.setEx(key, ttl, JSON.stringify(data));
      originJson(data);
    };
    next();
  };
}

module.exports = { cache, redis };
js 复制代码
// 使用缓存中间件
const express = require('express');
const { cache } = require('./cache-middleware');

const app = express();

app.get('/users', cache(30), async (req, res) => {
  // 首次请求查数据库,结果缓存 30 秒
  const users = [{ id: 1, name: '张三' }]; // 模拟 DB 查询
  res.json({ users });
});

app.listen(3000);
相关推荐
IT_陈寒2 小时前
JavaScript这5个隐藏技巧,90%的开发者都不知道!
前端·人工智能·后端
明月_清风3 小时前
小程序云函数:从入门到全栈的“降维打击”指南
前端·微信小程序·小程序·云开发
wuhen_n3 小时前
告别 Options API:为什么 Composition API 是逻辑复用的未来?
前端·javascript·vue.js
明月_清风3 小时前
前端异常捕获:从“页面崩了”到“精准定位”的实战架构
前端·javascript·监控
wuhen_n3 小时前
高效的数据解构:用 toRefs 和 toRef 保持响应性
前端·javascript·vue.js
小兵张健13 小时前
价值1000的 AI 工作流:Codex 通用前端协作模式
前端·aigc·ai编程
sunny_14 小时前
面试踩大坑!同一段 Node.js 代码,CJS 和 ESM 的执行顺序居然是反的?!99% 的人都答错了
前端·面试·node.js
拉不动的猪14 小时前
移动端调试工具VConsole初始化时的加载阻塞问题
前端·javascript·微信小程序
ayqy贾杰16 小时前
Agent First Engineering
前端·vue.js·面试