Node.js 是什么
这一课你将能做到
- 用一句话说清 Node.js 与浏览器 JavaScript 的区别,并指出 V8、libuv 各自负责什么
- 在本机确认
node -v / npm -v 可用,跑通 REPL 和第一个脚本文件
- 判断一个任务适不适合交给 Node(工具脚本、轻量服务、前端工程化)
为什么要学
你会写浏览器里的 JavaScript,但浏览器里没有 fs、不能监听端口、不能跑构建脚本。Node.js 人话:一个让 JavaScript 脱离浏览器、跑在操作系统上的运行时——它能读磁盘、起 HTTP 服务、执行命令行脚本。
本站业务 API 是 Spring Boot 写的,但 Next.js 构建、内容导入脚本、ESLint/Prettier 等前端工具链全部依赖 Node。学 Node 不是为了取代 Java 后端,而是打通「脚本 + 轻量服务」这条线:内容处理写脚本、原型验证起个小服务、排障时看得懂前端工程链路。
先建立图像
浏览器和 Node 共用同一个 JS 引擎(V8),差别在于「宿主」给你配了哪些能力:
┌──────────┐
你的 JS 代码 ─► V8 │ ← 编译执行 JavaScript(Chrome 也用它)
└────┬─────┘
▼
┌──────────┐
│ libuv │ ← 事件循环 + 线程池,提供异步 I/O
└────┬─────┘
▼
文件系统 / 网络 / 子进程(操作系统)
| 浏览器 | Node.js |
|---|
| 全局对象 | window / document | global / process |
| 操作 DOM | 有 | 没有 |
| 读文件 | 不行 | fs 模块 |
| 监听端口 | 不行 | http / net 模块 |
| 模块体系 | ESM 为主 | CommonJS 与 ESM 都常见 |
| 典型用途 | 页面交互 | 脚本、服务、工具链 |
一句话:同一种语言,不同的「底盘」。浏览器底盘装了方向盘(DOM),Node 底盘装了货箱(文件、网络、进程)。
跟着做
步骤 1:确认环境
打开终端(Windows 用 PowerShell):
node -v
npm -v
两条都应有版本输出(本课程基于 Node.js 20 LTS,例如 v20.x.x)。没有输出就去 nodejs.org 装 LTS 版;已装但版本低于 20 建议升级,避免新 API 跑不了。
团队项目要对齐大版本:一个用 18 一个用 20,就可能出现「我这边能跑你那边报错」。
步骤 2:玩一下 REPL
直接在终端输入 node 回车,进入交互式环境(REPL = 读取-求值-打印-循环):
> 1 + 1
2
> 'abc'.toUpperCase()
'ABC'
> const arr = [1, 2, 3]
undefined
> arr.map(n => n * 2)
[ 2, 4, 6 ]
> .exit
REPL 适合验证一行代码的行为(比如某个方法返回什么)。.exit 或 Ctrl+C 两次退出。REPL 是临时沙盒,正式代码一定写进文件。
步骤 3:跑第一个脚本
新建文件夹 node-first,在里面新建 hello.js:
// hello.js
const name = process.argv[2] ?? 'world';
console.log(`hello ${name}, node ${process.version}`);
node hello.js
# hello world, node v20.x.x
node hello.js Java
# hello Java, node v20.x.x
process.argv 是命令行参数数组:第 0 项是 node 可执行文件路径,第 1 项是脚本路径,从第 2 项开始才是你传的参数。?? 是空值合并:参数没传就用默认值。
步骤 4:初始化一个项目
{
"name": "node-first",
"version": "1.0.0",
"description": "",
"main": "hello.js",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1"
},
"keywords": [],
"author": "",
"license": "ISC"
}
"scripts": {
"start": "node hello.js"
}
npm start
# 等价于 node hello.js
npm run <名字> 是项目的统一入口:无论内部命令多长,团队都只需要记住 npm run xxx。
步骤 5:体验 Node 的「超能力」——读文件
在 node-first 里新建 note.txt,内容随意,比如 Node 真好玩。新建 read.js:
// read.js
const fs = require('node:fs');
const text = fs.readFileSync('note.txt', 'utf8');
console.log('文件内容:', text);
node read.js
# 文件内容: Node 真好玩
fs 是浏览器里不存在的能力——这就是「运行时」的含义:语言相同,能力边界由宿主决定。
讲清楚要点
- Node 是运行时,不是语言也不是框架:语言还是 JavaScript(ECMAScript),框架另有 Express 等。Node 的贡献是 V8 之外的整套能力:libuv 事件循环、fs/net/child_process 等内置模块、npm 生态。
- 单线程事件驱动 + 非阻塞 I/O:JS 主线程只有一个,I/O(读文件、网络请求)交给操作系统和 libuv 线程池异步完成,主线程不被卡住。这让 Node 用少量线程扛住大量并发连接——代价是 CPU 密集任务会阻塞整个进程(第 7 课展开)。
- 内置模块带
node: 前缀更稳:require('node:fs') 和 require('fs') 目前等价,但带前缀能明确「这是内置模块」,防止有人发布同名 npm 包来劫持,新代码统一加前缀。
- LTS 是首选版本线:LTS(Long Term Support)提供约 30 个月的安全维护,偶数版本(18、20、22)会转 LTS。生产环境装 LTS,尝鲜才用 Current。
- npm 是随 Node 一起装的包管理器:装包、跑脚本、管版本都靠它,第 3 课专门展开。
你可能会懵的地方
-
Node.js 和 Chrome 什么关系?
共用 V8 引擎解析执行 JS。Chrome 之后接的是渲染引擎和 DOM;Node 之后接的是 libuv 和系统能力。可以理解为同一个发动机装在两台不同的车上。
-
既然 Spring Boot 能写后端,为什么还要学 Node?
分工不同:复杂业务、强事务的系统 Java 更稳;但脚本工具(批量处理内容)、前端工程链(构建、lint)、轻量 BFF/API 网关是 Node 的主场。全栈团队通常两者都用。
-
Deno 和 Bun 是什么?要换吗?
同类运行时,各有亮点(Deno 默认 TS、Bun 启动快)。但 Node 生态最成熟、岗位最多,初学阶段押注 Node 不会有错;理解了 Node 再看它们,一天就能上手。
-
Node 适合做 CPU 密集的事吗?
不适合。图像压缩、大规模运算会卡住单线程事件循环,这类任务用 Java/Go/Python,或交给 Node 的子进程/Worker(进阶内容)。Node 的甜区是 I/O 密集:大量网络请求、文件读写。
-
node hello.js 时 hello.js 必须在当前目录吗?
不是。可以写相对路径 node scripts/hello.js 或绝对路径。但脚本内部再用相对路径读文件时,参照物是「终端当前目录」而不是脚本所在目录——这是新手第一大坑,第 2 课用 process.cwd() 和 __dirname 解决。
错法对照
| 错法 | 问题 | 正解 |
|---|
在 Node 里用 document、window | 浏览器专属对象,Node 没有 | 用 global / process / 内置模块 |
| 生产环境装 Current 最新版 | 非 LTS,支持周期短 | 装 LTS(Node 20)并对齐团队版本 |
| 把 REPL 当编辑器写长代码 | 不可保存、不可复现 | 写 .js 文件再 node xxx.js |
require('fs') 不带前缀 | 可能被同名第三方包劫持 | require('node:fs') |
所有逻辑塞进一行 node -e "..." | 转义地狱、无法维护 | -e 只做冒烟验证,逻辑进文件 |
卡点急救
node 不是内部或外部命令:Node 没装或 PATH 没生效。Windows 重开终端;还不行就把 Node 安装目录加进系统 PATH。
- 版本号比 20 低很多:官网重装 LTS 版即可覆盖旧版本;用 nvm-windows / nvm 管理多版本更省心。
Cannot find module 'xxx':检查文件名拼写(大小写敏感)和当前目录;确实在用第三方包的话,先 npm install xxx。
- npm init 报错或生成失败:多半在无权限目录执行了命令,换到用户目录下的项目文件夹。
面试常问
-
Node.js 是什么?由哪几部分组成?
基于 V8 的 JavaScript 运行时。核心组成:V8(执行 JS)、libuv(事件循环 + 异步 I/O + 线程池)、内置模块(fs/net/http 等)、npm 生态。特点是单线程、事件驱动、非阻塞 I/O,适合 I/O 密集场景。
-
Node 为什么单线程也能扛高并发?
阻塞操作(网络、磁盘)不占用 JS 主线程,由 libuv 异步调度,主线程只处理回调。一个线程避免锁和上下文切换开销,配合事件循环能维持数万并发连接。短板是 CPU 密集任务会阻塞。
-
LTS 和 Current 的区别?
Current 包含最新特性,仅维护 6 个月;LTS 从 Current 演进而来,提供约 30 个月支持。生产环境用 LTS,保证安全补丁和 API 稳定。
自测三问
- V8 和 libuv 分别负责什么?
- 浏览器 JS 和 Node JS 各举出两样「对方没有」的能力。
node hello.js Java 时,process.argv 的第 0、1、2 项分别是什么?
小结一句话
Node.js = V8 引擎 + libuv + 系统能力,让 JavaScript 离开浏览器去读文件、听端口、跑脚本;它的甜区是 I/O 密集任务和工程化工具链。
延伸