Node.js

Node.js 是基于 Chrome V8 引擎的 JavaScript 运行时,将 JS 的执行环境从浏览器扩展到服务端。核心特征是单线程事件循环 + 非阻塞 I/O,擅长处理高并发 I/O 密集型任务。

版本管理推荐使用 nvm,包管理可选 npmyarnpnpm


安装

# 推荐:通过 nvm 安装,支持多版本切换
nvm install 22          # 安装 LTS 版本
nvm use 22
node --version          # 验证
 
# 直接下载安装包(固定版本)
# https://nodejs.org/zh-cn/download/

Node.js 版本分为:

  • LTS(长期支持):生产环境首选,维护周期 30 个月
  • Current:包含最新特性,适合尝鲜

事件循环

Node.js 的并发模型基于事件循环,单线程处理所有请求,I/O 操作委托给底层 libuv 异步执行:

   ┌───────────────────────┐
   │        timers          │  ← setTimeout / setInterval 回调
   ├───────────────────────┤
   │     pending callbacks  │  ← 上一轮 I/O 错误回调
   ├───────────────────────┤
   │       idle, prepare    │  ← 内部使用
   ├───────────────────────┤
   │          poll          │  ← 获取新 I/O 事件(核心阶段)
   ├───────────────────────┤
   │          check         │  ← setImmediate 回调
   ├───────────────────────┤
   │     close callbacks    │  ← socket.on('close') 等
   └───────────────────────┘

微任务(Promise.thenqueueMicrotask)在每个阶段切换前执行,优先级高于宏任务。

console.log('1');
setTimeout(() => console.log('setTimeout'), 0);
Promise.resolve().then(() => console.log('Promise'));
console.log('2');
// 输出顺序:1 → 2 → Promise → setTimeout

模块系统

CommonJS(CJS)— 传统方式

// 导出
const add = (a, b) => a + b;
module.exports = { add };
// 或
exports.add = add;
 
// 导入(同步,运行时解析)
const { add } = require('./math');
const fs = require('fs');       // 内置模块
const lodash = require('lodash'); // npm 包

ES Modules(ESM)— 现代方式

// 文件扩展名用 .mjs,或 package.json 设置 "type": "module"
 
// 导出
export const add = (a, b) => a + b;
export default class Calculator { ... }
 
// 导入(静态,编译时解析;支持 Tree Shaking)
import { add } from './math.js';
import Calculator from './math.js';
 
// 动态导入(按需加载)
const { add } = await import('./math.js');

.cjs 强制 CommonJS,.mjs 强制 ESM,package.json"type": "module".js 默认为 ESM。


内置模块

fs — 文件系统

import { readFile, writeFile, readdir, stat, mkdir, unlink } from 'fs/promises';
 
// 读文件
const content = await readFile('./config.json', 'utf-8');
const config = JSON.parse(content);
 
// 写文件
await writeFile('./output.txt', 'Hello World', 'utf-8');
 
// 目录操作
const files = await readdir('./src');
await mkdir('./dist', { recursive: true });  // 递归创建
 
// 文件信息
const info = await stat('./package.json');
console.log(info.size, info.mtime);
 
// 流式读取大文件(避免内存溢出)
import { createReadStream } from 'fs';
const stream = createReadStream('./large.csv', { encoding: 'utf-8' });
for await (const chunk of stream) {
    process(chunk);
}

path — 路径处理

import path from 'path';
 
path.join('/foo', 'bar', 'baz.txt')   // '/foo/bar/baz.txt'(跨平台)
path.resolve('src', 'index.js')       // 返回绝对路径
path.dirname('/foo/bar/baz.txt')      // '/foo/bar'
path.basename('/foo/bar/baz.txt')     // 'baz.txt'
path.extname('index.html')            // '.html'
 
// ESM 中获取当前文件目录(替代 __dirname)
import { fileURLToPath } from 'url';
const __dirname = path.dirname(fileURLToPath(import.meta.url));

http / https — 原生 HTTP 服务

import http from 'http';
 
const server = http.createServer((req, res) => {
    if (req.method === 'GET' && req.url === '/health') {
        res.writeHead(200, { 'Content-Type': 'application/json' });
        res.end(JSON.stringify({ status: 'ok' }));
        return;
    }
    res.writeHead(404);
    res.end('Not Found');
});
 
server.listen(3000, () => console.log('Server running on :3000'));

实际项目通常使用 Express / Koa / Fastify 等框架,而非直接使用原生 http 模块。

events — 事件发射器

import { EventEmitter } from 'events';
 
class DownloadTask extends EventEmitter {
    async start(url) {
        this.emit('start', url);
        try {
            const data = await fetch(url);
            this.emit('progress', 50);
            // ... 处理数据
            this.emit('done', data);
        } catch (err) {
            this.emit('error', err);
        }
    }
}
 
const task = new DownloadTask();
task.on('start', (url) => console.log('开始下载:', url));
task.on('progress', (pct) => console.log(`进度: ${pct}%`));
task.on('done', (data) => console.log('完成'));
task.on('error', (err) => console.error('失败:', err));

crypto — 加密

import crypto from 'crypto';
 
// 哈希
const hash = crypto.createHash('sha256').update('password').digest('hex');
 
// HMAC 签名
const sign = crypto.createHmac('sha256', 'secret').update('data').digest('hex');
 
// 生成随机 Token
const token = crypto.randomBytes(32).toString('hex');
 
// UUID v4
const { randomUUID } = crypto;
const id = randomUUID();  // 'a1b2c3d4-...'

child_process — 子进程

import { exec, execFile, spawn } from 'child_process';
import { promisify } from 'util';
 
const execAsync = promisify(exec);
 
// 执行 shell 命令(小输出)
const { stdout } = await execAsync('git log --oneline -5');
console.log(stdout);
 
// spawn 处理大输出(流式)
const child = spawn('node', ['worker.js']);
child.stdout.on('data', (data) => console.log(data.toString()));
child.on('close', (code) => console.log(`退出码: ${code}`));

全局对象

// 进程信息
process.env.NODE_ENV          // 环境变量
process.argv                   // 命令行参数([node, script, ...args])
process.cwd()                  // 当前工作目录
process.exit(0)                // 退出进程
process.on('uncaughtException', handler)  // 全局异常捕获
 
// 计时器
setTimeout(fn, 1000)
setInterval(fn, 1000)
setImmediate(fn)               // 当前事件循环末尾执行
queueMicrotask(fn)             // 微任务队列
 
// 全局对象(ESM 中可用)
globalThis.myVar = 'shared';

错误处理

// 异步函数统一 try/catch
async function fetchUser(id) {
    try {
        const res = await fetch(`/api/users/${id}`);
        if (!res.ok) throw new Error(`HTTP ${res.status}`);
        return await res.json();
    } catch (err) {
        console.error('fetchUser 失败:', err.message);
        throw err;    // 向上传播,不要静默吞掉
    }
}
 
// 未捕获的 Promise 异常
process.on('unhandledRejection', (reason, promise) => {
    console.error('未处理的 Promise 拒绝:', reason);
    process.exit(1);
});
 
// 同步未捕获异常
process.on('uncaughtException', (err) => {
    console.error('未捕获异常:', err);
    process.exit(1);
});

Worker Threads(多线程)

Node.js 主线程单线程,CPU 密集型任务用 Worker 线程并行:

import { Worker, isMainThread, parentPort, workerData } from 'worker_threads';
 
if (isMainThread) {
    const worker = new Worker(import.meta.url, {
        workerData: { numbers: [1, 2, 3, 4, 5] }
    });
    worker.on('message', (result) => console.log('结果:', result));
} else {
    const sum = workerData.numbers.reduce((a, b) => a + b, 0);
    parentPort.postMessage(sum);
}

常用 CLI 命令

node index.js                   # 执行脚本
node --watch index.js           # 文件变更自动重启(Node 18+)
node -e "console.log(1+1)"      # 执行单行代码
node --inspect index.js         # 启用调试器(配合 Chrome DevTools)
 
# 查看版本
node --version
node -v
 
# REPL 交互环境
node
> 2 + 2
4

相关链接

  • nvm — Node.js 版本管理,多版本切换
  • npm — 默认包管理器
  • yarn — 替代包管理器(速度快、锁文件稳定)
  • pnpm — 高效包管理器(硬链接复用,节省磁盘)
  • IDEA — IntelliJ IDEA 内置 Node.js 调试支持
  • VS Code — Node.js 开发首选编辑器