核心模块
约 1330 字大约 4 分钟
nodecore
2026-04-08
目录:
Node.js 的核心模块是其运行时直接内置、无需 npm 安装即可 require/import 的一组模块。理解它们是写好 Node 服务端、CLI、构建工具的基础。
Event Loop 与事件队列
Node 基于 libuv 的事件循环以单线程方式驱动 I/O。事件循环划分为多个阶段(phases),每个阶段都有自己的 FIFO 回调队列:
| 阶段 | 处理内容 |
|---|---|
| timers | setTimeout / setInterval 到期回调 |
| pending callbacks | 上一轮挂起的系统级回调(如 TCP 错误) |
| idle, prepare | 内部使用 |
| poll | 取出新 I/O 事件并执行其回调(核心阶段) |
| check | setImmediate 回调 |
| close callbacks | socket.on('close', ...) 等关闭事件 |
每个宏任务之间还会清空:
process.nextTick队列(优先级最高)- 微任务队列(Promise.then / queueMicrotask)
setTimeout(() => console.log('timeout'), 0)
setImmediate(() => console.log('immediate'))
Promise.resolve().then(() => console.log('promise'))
process.nextTick(() => console.log('nextTick'))
// nextTick -> promise -> timeout/immediate(顺序与所在阶段有关)与浏览器事件循环的核心区别:Node 的微任务是在每个阶段、每个回调之间都清空一次;浏览器是在每个宏任务结束时清空。
process 全局对象
process 提供进程级 API,常用:
process.argv—— 命令行参数(CLI 工具的入口)process.env—— 环境变量process.cwd()/process.chdir()—— 当前工作目录process.pid/process.ppidprocess.exit(code)—— 退出进程process.on('uncaughtException' | 'unhandledRejection' | 'SIGINT' | 'exit', cb)—— 进程级事件钩子process.nextTick(cb)—— 在当前阶段末尾、微任务之前插入回调process.memoryUsage()/process.cpuUsage()—— 性能观测
events 事件处理模块
EventEmitter 是 Node 内异步通信的基石,fs/net/stream/http 都是它的子类。
import { EventEmitter } from 'node:events'
const bus = new EventEmitter()
bus.on('data', (chunk) => console.log(chunk))
bus.once('done', () => console.log('only once'))
bus.emit('data', 'hello')要点:
- 同步派发:
emit会同步调用所有监听器 - 默认最多 10 个监听器,超过会警告,可通过
setMaxListeners调整 - 错误事件
error必须有监听器,否则进程崩溃 - 可使用
once(emitter, 'event')把事件 Promise 化
CommonJS 原理
CommonJS 是 Node 最早的模块规范,require 加载流程:
- 路径解析(核心模块 → 文件 → 目录 → node_modules 向上查找)
- 缓存检查(
require.cache) - 编译执行:将文件源码包裹进函数
(function (exports, require, module, __filename, __dirname) { ... }) - 返回
module.exports
因此每个模块都有独立作用域,且 module.exports 是值拷贝,循环依赖可能拿到未完成的对象。ESM (import) 与 CJS 互操作时需要注意 default 与 namespace 的差异。
字符编码与 Buffer
JavaScript 字符串是 UTF-16,但 Node 处理网络/文件时面对的是字节流,需要 Buffer:
const buf = Buffer.from('你好', 'utf-8') // <Buffer e4 bd a0 e5 a5 bd>
buf.toString('utf-8') // '你好'
buf.length // 6(字节数,不是字符数)要点:
Buffer是 Uint8Array 的子类,分配在堆外,避免 V8 GC 抖动- 常见编码:
utf-8、ascii、base64、hex、latin1 - 大块数据操作优先使用
Buffer.allocUnsafe+ 手动覆盖以避免清零开销 - 处理多字节字符时不要按字节切片,否则会出现乱码
fs 文件模块
node:fs 提供同步 / 回调 / Promise 三套 API,推荐使用 node:fs/promises:
import { readFile, writeFile, stat } from 'node:fs/promises'
const data = await readFile('./pkg.json', 'utf-8')
await writeFile('./out.json', data)
const info = await stat('./pkg.json')进阶能力:
createReadStream/createWriteStream—— 大文件流式处理watch—— 文件监听(开发服务器、热更新)fs.constants—— 权限位与 flagcp/rm—— 现代化的复制与递归删除
压缩与解压缩
node:zlib 提供 gzip / deflate / brotli 等常见算法,常与 stream 配合:
import { createReadStream, createWriteStream } from 'node:fs'
import { createGzip } from 'node:zlib'
import { pipeline } from 'node:stream/promises'
await pipeline(
createReadStream('app.log'),
createGzip(),
createWriteStream('app.log.gz')
)HTTP 服务的响应压缩、构建产物压缩都建立在 zlib 之上。
stream 流的原理与应用
Stream 是 Node 高效处理 I/O 的基石,分四类:
| 类型 | 说明 | 示例 |
|---|---|---|
| Readable | 可读流 | fs.createReadStream |
| Writable | 可写流 | fs.createWriteStream |
| Duplex | 双工流 | net.Socket |
| Transform | 双工 + 转换 | zlib.createGzip |
核心概念:
- 背压 (backpressure):消费速度跟不上生产速度时通过
pause/resume、drain事件协调 - 管道 (pipeline):用
stream.pipeline串联流并统一处理错误,优于pipe - 对象模式:
objectMode: true时流中传递任意 JS 对象 - 高水位线 highWaterMark:内部缓冲区大小阈值
cookie 和 session 原理
HTTP 是无状态协议,服务端通过两种机制识别用户:
Cookie:服务端在响应头 Set-Cookie 写入键值,浏览器自动在后续请求的 Cookie 头中带回。关键属性:
HttpOnly—— 禁止 JS 读取,防 XSS 窃取Secure—— 仅 HTTPS 传输SameSite—— Lax / Strict / None,防 CSRFDomain/Path/Max-Age
Session:服务端在内存 / Redis 中存储用户状态,仅向客户端写入一个 sessionId(通常通过 Cookie)。请求到达时根据 sessionId 还原状态。
JWT 是无状态的替代方案:把"会话状态"签名后存放在客户端,服务端只需验签,省掉存储但失去主动失效能力。
小结
Node 核心模块的核心心智模型只有两点:
- 一切异步皆事件:通过 EventEmitter / 事件循环把 I/O 转化为事件回调
- 一切数据皆流:通过 Buffer + Stream 在不占用大量内存的前提下处理任意大小的数据
掌握这两点后再去看 http、net、cluster、worker_threads 等更高级的模块,会有种"万变不离其宗"的顺畅感。
