本节摘要:Node.js 是一个基于 V8 引擎与 libuv 事件循环的 JavaScript 运行时,用「单线程 + 非阻塞 I/O」模型处理高并发网络请求。本节讲清它的分层构成、与传统线程模型的对比、适用与不适用的场景,并完成环境搭建与第一个 HTTP 服务器的实操。
很多人第一反应是:「Node.js 就是跑在服务器上的浏览器 JavaScript。」这个理解差了一层。浏览器里的 JavaScript 是一门被沙箱圈起来的语言,摸不到文件系统和网络底层;而 Node.js 做的事情是把 V8 引擎从浏览器里搬出来,再往它身上接一整套操作系统级的能力——读写文件、监听端口、创建子进程、加密解密。所以更准确的说法是:Node.js 是一个让 JavaScript 获得操作系统接口的运行时。
它的构成可以拆成四块:
传统服务器(比如早期 Apache 的默认配置)采用「每个请求分配一个线程」的模型。一百个并发请求就是一百个线程,每个线程默认预留约 1MB 栈空间,光内存就要一百多 MB;线程之间的上下文切换还要消耗 CPU。而 Node 只有一条主线程处理所有请求逻辑,I/O 操作交给操作系统内核与 libuv 线程池,完成后把回调「排队」送回主线程。

这张图也交代了 Node 的软肋:因为只有一条主线程,任何一段同步的重计算都会让全部请求排队。第 7 章讲的 cluster 与 Worker Threads 就是给这个软肋打的补丁。
安装 Node 的方式很多,我最推荐用版本管理器:Node 大版本每年两次迭代,项目多了之后你一定需要在同一台机器上切换版本。
装好后验证:
node -v npm -v
两个命令分别输出版本号即可。接着花两分钟认识 REPL——在终端里直接敲 node 回车,进入交互式环境,可以随手验证表达式:
$ node > 1 + 1 2 > [1,2,3].map(x => x * 2) [ 2, 4, 6 ] > .exit
REPL 是贯穿本书的「草稿纸」:后面讲事件循环执行顺序时,所有实验都可以先在这里跑。
七行代码,一个能用的 Web 服务器:
const http = require('http'); const server = http.createServer((req, res) => { res.writeHead(200, { 'Content-Type': 'text/plain; charset=utf-8' }); res.end('你好,Node'); }); server.listen(3000, () => { console.log('服务器已启动,端口 3000'); });
运行后浏览器访问本机 3000 端口就能看到问候。注意这段代码里没有「启动线程」的动作——createServer 只是注册了一个回调:每当有请求进来,事件循环就调用它一次。一千个请求同时到达?没关系,它们只是排队等着同一个回调被调用一千次,而回调内部的 res.end 之类的 I/O 又是非阻塞的。
这里已经埋下了全书的伏笔:回调函数是 Node 与事件循环之间唯一的接口。它为什么会长成这样、怎么避免越写越乱,就是第 2 章的主题。
⚠️ 常见坑:在回调里写同步的重计算(比如 JSON.parse 一个 50MB 的文件)会让所有并发请求一起卡住。从第一天起就要建立「主线程寸土寸金」的意识。