Node系列 · Node基础Node.js 概述Node.js 不是跑在服务器上的 JavaScript而是一套独立的运行时Runtime。理解它和浏览器的边界就能解释为什么它能做高并发 IO、为什么不能做 CPU 密集型任务、为什么需要package.json而浏览器页面不需要。一、Node.js 是什么Node.js是基于V8 引擎libuv 一组内置核心模块fs、http、net、crypto等的 JavaScript 运行时主要用于在操作系统层执行 JavaScript 代码。Node.js 运行时应用代码JavaScript业务逻辑第三方包npm内置模块 requireV8 引擎编译 执行 JSlibuv事件循环 / 异步 IO / 线程池内置 C 绑定fs / net / crypto ...操作系统 / 内核组件角色V8Google 的 JS 引擎负责把 JS 编译为机器码并执行。语法、数据类型、执行模型由 ECMAScript 规范定义V8 是规范的实现libuv用 C 写的事件循环库封装了异步 IO、线程池、信号、子进程屏蔽操作系统差异内置模块用 C 实现绑定到 JS 层对外暴露require(fs)这类 API::: infoNode.js 不是前端框架也不属于任何一门后端语言。它是一个宿主环境——和浏览器宿主环境平行。浏览器宿主提供 DOM、BOM、FetchNode 宿主提供fs、http、process。:::二、和浏览器运行时的核心差异Node 运行时和浏览器运行时的差异决定了它能做什么、不能做什么。这一节从 6 个核心维度对比维度浏览器Node.js主要宿主对象window/documentglobal/globalThis/process默认入口HTML script标签.js文件 package.json的main内置模块DOM、BOM、Fetch、Storagefs、http、net、path、crypto、stream模块系统ESMscript typemoduleCommonJS默认 ESM需.mjs或package.json加type: module多线程Web Worker受限Worker Threads 真正的多进程进程控制不能主动退出process.exit()/process.kill()/ 子进程权限模型沙箱同源策略本机用户权限文件 / 网络完全开放::: warningNode.js 拥有本机文件系统与网络权限。任何被执行的 JS 都可以读写硬盘、监听端口、调用子进程。这一点与浏览器完全相反——不要把来源不明的node script.js当成只是跑个 JS。:::三、单线程与异步 IONode.js 主线程是单线程JS 代码始终跑在同一个调用栈上。它能扛高并发的关键不是CPU 多核而是IO 操作全部异步 事件循环调度。文件系统libuv 线程池主线程JS文件系统libuv 线程池主线程JS主线程立即继续执行fs.readFile(a.txt, cb)fs.readFile(b.txt, cb)读 a.txt读 b.txta.txt 内容触发 a 的回调b.txt 内容触发 b 的回调关键结论CPU 密集任务同步循环、加密、压缩、JSON 序列化大对象会阻塞整个事件循环导致所有请求排队等待IO 密集任务文件读写、网络请求、数据库查询天然适合 Node——内核和线程池处理主线程只关心回调::: tip 一句话判断该不该用 Node“请求量高、每次请求等待外部资源DB / 第三方 API / 文件多、本身的计算逻辑轻”——这种场景 Node 是首选。反过来如果业务是图像处理、视频转码、复杂数学运算应当把重计算交给专用服务Node 只负责编排。:::基于上面的架构特点可以反过来判断什么场景 Node 不擅长这是下一节的主题。四、适用与不适用场景适合Web API / BFF 层Express、Koa、NestJS、Fastify 这类框架是 Node 最成熟的领域实时通信WebSocket 服务聊天、协作、白板中间层 / 网关聚合多个下游接口、做协议转换CLI 工具前端构建工具Vite、webpack、脚手架create-vue、npm init轻量脚本爬虫、定时任务、数据迁移不擅长CPU 重计算图像处理、视频转码、科学计算、AI 推理。原因是主线程一旦被长任务占据所有请求都得排队——CPU 密集场景的并发收益会被单线程结构吞掉强事务一致性的金融核心系统这类业务依赖成熟的分布式事务框架、关系型数据库生态、运维工具链Java 系在该领域积累更深Node 生态的相关基础设施相对薄弱生产案例少::: info“不擅长不是不能做”。Node 完全可以通过 Worker Threads、子进程或外部服务调用来扩展能力——只是相比 Go、Rust、Java 这些原生支持并发的语言性价比更低。:::五、一次完整的 Node 执行流程console.log(1. 同步执行开始); setTimeout(() { console.log(3. 宏任务回调timers 阶段); }, 0); Promise.resolve().then(() { console.log(2. 微任务promise); }); console.log(4. 同步执行结束);$nodehello.js1. 同步执行开始4. 同步执行结束2. 微任务promise3. 宏任务回调timers 阶段事件循环按阶段推进每个阶段处理一类宏任务阶段切换前清空微任务是否同步代码清空微任务队列nextTick Promise.thentimers 阶段setTimeout / setIntervalpending callbacksidle, preparepoll 阶段I/O 回调 / 阻塞等待新 I/Ocheck 阶段setImmediateclose callbacks清空微任务队列还有任务?进程退出每个节点的真实行为同步代码JS 主线程自上而下立即执行的代码。这是整个流程唯一真正在 V8 上跑的环节。遇到setTimeout、Promise、fs.readFile等异步调用时不会阻塞——注册回调后立即返回。进入循环前的微任务队列同步代码全部跑完后、事件循环进入timers之前会一次性清空微任务队列。process.nextTick的优先级高于Promise.then——同一轮里所有 nextTick 全部跑完才会轮到 Promise。process.nextTick(()console.log(nextTick));Promise.resolve().then(()console.log(promise));// 输出nextTick → promisetimers 阶段执行setTimeout和setInterval注册的回调。这里有两个隐藏约束最小延迟 1ms即使写setTimeout(fn, 0)也不会在 0ms 时触发会被规范强制抬到 1ms不保证精确轮询到 timers 时只取已经到期的回调如果 poll 阶段耗时较长下一次 timers 触发会跟着推迟pending callbacks执行延迟到下一个循环迭代的 I/O 回调例如某些系统级错误回调。日常业务代码几乎碰不到多数 Node 开发者可以忽略此阶段。idle, prepareNode 内部使用的阶段不对外暴露。libuv 在这里做一些空闲时的优化工作。用户代码不会在这里执行。poll 阶段事件循环最核心的阶段。它做两件事执行 poll 队列里已经到期的 I/O 回调fs.readFile、net的 data 事件等若所有阶段都没有更多回调timers 队列空、check 队列空、poll 队列空poll 会阻塞等待新 I/O阻塞等待不是 bug而是设计——Node 不会在无事可做时浪费 CPU。只有当这些条件之一满足时poll 才会停止等待timers 队列有到期回调setImmediate注册了新任务poll 队列本身有新回调check 阶段执行setImmediate注册的回调。这是 Node 提供的下一轮立即执行机制——不经过 timers 的 1ms 最小延迟。setImmediate经常和setTimeout(fn, 0)拿来比较在 I/O 回调内部前者总是比后者先触发因为 poll 阶段之后紧跟 check而 timers 要等到下一轮。close callbacks执行socket.on(close, ...)、process.exit()后的清理回调等关闭事件。阶段后的微任务清空每个宏任务阶段执行完毕、即将进入下一阶段时会再清空一次微任务队列。这保证了微任务总是优先于下一个宏任务阶段。循环判断如果 timers、check、poll 三个队列都为空事件循环就退出进程随之结束。常见的退出场景所有异步 I/O 完成 没有定时器 没有 setImmediate显式调用process.exit()这个模型会贯穿后续所有章节——理解它就理解了为什么setImmediate不一定比setTimeout(fn, 0)快、为什么文件读取回调默认在 poll 阶段触发。六、Node.js 版本选择版本状态何时用Current如 24.x当前开发版想用最新语法、fetch、内置测试运行器LTS如 22.x Iron长期维护推荐生产环境、本系列全部示例EOL已停止维护升级本文所有示例在Node.js 22 LTS验证通过除明确标注外不依赖任何实验性 API。七、小结Node.js V8 libuv 内置模块是 JS 的服务端运行时不是后端语言单线程 异步 IO 事件循环是其架构核心CPU 密集场景需要外移拥有本机权限不要把node script.js当成沙箱里跑 JS选择版本优先 LTS本文使用 Node 22