Node.js 基础
大约 10 分钟约 3071 字
Node.js 基础
简介
Node.js 是基于 Chrome V8 引擎的 JavaScript 运行时,让 JavaScript 不再只能运行在浏览器里,也可以运行在服务端、命令行工具和构建脚本中。它最重要的价值不只是“能写后端”,而是提供了一套围绕异步 I/O、模块系统、包管理和工具链构建起来的完整运行时生态。
特点
实现
模块系统:CommonJS 与 ESM
// math.cjs - CommonJS
function add(a, b) {
return a + b
}
function multiply(a, b) {
return a * b
}
module.exports = {
add,
multiply,
}// app.cjs
const { add, multiply } = require('./math.cjs')
console.log(add(2, 3))
console.log(multiply(4, 5))// utils.mjs - ES Module
export function formatDate(date) {
return date.toISOString().slice(0, 10)
}
export const API_BASE = 'https://api.example.com'
export default function log(message) {
console.log(`[LOG] ${message}`)
}// main.mjs
import log, { formatDate, API_BASE } from './utils.mjs'
log(`today = ${formatDate(new Date())}`)
console.log(API_BASE)文件系统与路径处理
import fs from 'node:fs/promises'
import path from 'node:path'
import { fileURLToPath } from 'node:url'
const __filename = fileURLToPath(import.meta.url)
const __dirname = path.dirname(__filename)
async function loadConfig() {
const configPath = path.join(__dirname, 'config.json')
const content = await fs.readFile(configPath, 'utf-8')
return JSON.parse(content)
}
const config = await loadConfig()
console.log(config)// 写文件与目录遍历
import fs from 'node:fs/promises'
await fs.writeFile('./output.json', JSON.stringify({ ok: true }, null, 2), 'utf-8')
const files = await fs.readdir('./src')
const jsFiles = files.filter(file => file.endsWith('.js') || file.endsWith('.ts'))
console.log(jsFiles)// 判断文件是否存在,尽量不要先 exists 再 read,优先直接 try/catch
try {
const content = await fs.readFile('./data.txt', 'utf-8')
console.log(content)
} catch (error) {
console.error('读取文件失败:', error)
}原生 HTTP 服务
import http from 'node:http'
const server = http.createServer((req, res) => {
const { method, url } = req
if (method === 'GET' && url === '/api/health') {
res.writeHead(200, { 'Content-Type': 'application/json; charset=utf-8' })
res.end(JSON.stringify({ status: 'ok', uptime: process.uptime() }))
return
}
if (method === 'GET' && url === '/api/version') {
res.writeHead(200, { 'Content-Type': 'application/json; charset=utf-8' })
res.end(JSON.stringify({ node: process.version }))
return
}
res.writeHead(404, { 'Content-Type': 'text/plain; charset=utf-8' })
res.end('Not Found')
})
server.listen(3000, () => {
console.log('server started at http://localhost:3000')
})// 处理 POST JSON
import http from 'node:http'
const server = http.createServer((req, res) => {
if (req.method === 'POST' && req.url === '/api/users') {
let body = ''
req.on('data', chunk => {
body += chunk
})
req.on('end', () => {
const data = JSON.parse(body)
res.writeHead(201, { 'Content-Type': 'application/json; charset=utf-8' })
res.end(JSON.stringify({ id: 1, ...data }))
})
return
}
res.writeHead(404)
res.end('Not Found')
})
server.listen(3001)事件循环与异步模型
// 事件循环执行顺序:同步代码 -> 微任务 -> 宏任务
console.log('1. 同步代码')
// 微任务:Promise.then、queueMicrotask、MutationObserver
Promise.resolve().then(() => {
console.log('3. 微任务 Promise')
})
queueMicrotask(() => {
console.log('4. 微任务 queueMicrotask')
})
// 宏任务:setTimeout、setInterval、setImmediate(Node.js)、I/O 回调
setTimeout(() => {
console.log('5. 宏任务 setTimeout')
}, 0)
process.nextTick(() => {
console.log('2. nextTick(优先级高于微任务)')
})
// 输出顺序:1 -> 2 -> 3 -> 4 -> 5// async/await 本质是 Promise 的语法糖
async function fetchUserData(userId) {
try {
// await 之后的代码相当于 .then 回调(微任务)
const response = await fetch(`/api/users/${userId}`)
const data = await response.json()
return data
} catch (error) {
console.error('请求失败:', error.message)
throw error
}
}
// 并发请求 — Promise.all vs Promise.allSettled
async function fetchAllData() {
// Promise.all:任一失败则整体失败
const [users, posts] = await Promise.all([
fetch('/api/users').then(r => r.json()),
fetch('/api/posts').then(r => r.json()),
])
// Promise.allSettled:全部完成,无论成功失败
const results = await Promise.allSettled([
fetch('/api/users').then(r => r.json()),
fetch('/api/comments').then(r => r.json()),
fetch('/api/likes').then(r => r.json()),
])
results.forEach((result, index) => {
if (result.status === 'fulfilled') {
console.log(`请求 ${index} 成功:`, result.value)
} else {
console.error(`请求 ${index} 失败:`, result.reason)
}
})
}Worker Threads — CPU 密集型任务
// main.mjs — 主线程
import { Worker } from 'node:worker_threads'
import path from 'node:path'
import { fileURLToPath } from 'node:url'
const __dirname = path.dirname(fileURLToPath(import.meta.url))
function runInWorker(taskFn, data) {
return new Promise((resolve, reject) => {
const worker = new Worker(path.join(__dirname, 'worker.mjs'), {
workerData: data
})
worker.on('message', resolve)
worker.on('error', reject)
worker.on('exit', (code) => {
if (code !== 0) reject(new Error(`Worker 停止,退出码: ${code}`))
})
})
}
// 使用 Worker 处理 CPU 密集型任务
const result = await runInWorker('computeFibonacci', { n: 40 })
console.log('计算结果:', result)// worker.mjs — 工作线程
import { workerData, parentPort } from 'node:worker_threads'
function fibonacci(n) {
if (n <= 1) return n
return fibonacci(n - 1) + fibonacci(n - 2)
}
// 接收任务并执行
const result = fibonacci(workerData.n)
parentPort.postMessage({ result })环境变量与配置管理
// config/index.mjs — 配置管理
const env = process.env.NODE_ENV || 'development'
const config = {
development: {
port: 3000,
db: { host: 'localhost', port: 5432 },
logLevel: 'debug',
cors: true,
},
production: {
port: 8080,
db: { host: process.env.DB_HOST, port: Number(process.env.DB_PORT) },
logLevel: 'info',
cors: false,
},
}
const currentConfig = config[env]
if (!currentConfig) {
throw new Error(`未知环境: ${env}`)
}
export default currentConfig
// .env 文件(配合 dotenv 使用)
// NODE_ENV=development
// DB_HOST=localhost
// DB_PORT=5432
// API_KEY=your-secret-key// 进程管理
console.log('进程 ID:', process.pid)
console.log('Node 版本:', process.version)
console.log('工作目录:', process.cwd())
console.log('内存使用:', process.memoryUsage())
// 优雅退出
process.on('SIGTERM', () => {
console.log('收到 SIGTERM 信号,准备关闭...')
// 关闭数据库连接、清理资源
server.close(() => {
console.log('服务已关闭')
process.exit(0)
})
})
process.on('uncaughtException', (err) => {
console.error('未捕获的异常:', err)
// 记录日志后优雅退出
process.exit(1)
})
process.on('unhandledRejection', (reason, promise) => {
console.error('未处理的 Promise 拒绝:', reason)
})Stream 与大文件处理
import fs from 'node:fs'
const readStream = fs.createReadStream('./big.log', { encoding: 'utf-8' })
const writeStream = fs.createWriteStream('./filtered.log', { encoding: 'utf-8' })
readStream.on('data', chunk => {
if (chunk.includes('ERROR')) {
writeStream.write(chunk)
}
})
readStream.on('end', () => {
writeStream.end()
console.log('done')
})// pipeline 更适合生产场景
import fs from 'node:fs'
import { pipeline } from 'node:stream/promises'
import zlib from 'node:zlib'
await pipeline(
fs.createReadStream('./access.log'),
zlib.createGzip(),
fs.createWriteStream('./access.log.gz')
)// Transform 流 — 自定义数据处理
import { Transform } from 'node:stream'
class CsvToJson extends Transform {
constructor() {
super({ objectMode: true })
this.headers = null
}
_transform(chunk, encoding, callback) {
const line = chunk.toString().trim()
if (!line) return callback()
const values = line.split(',')
if (!this.headers) {
this.headers = values
return callback()
}
const obj = {}
this.headers.forEach((header, i) => {
obj[header.trim()] = values[i]?.trim()
})
this.push(obj)
callback()
}
}
// 使用
import { pipeline } from 'node:stream/promises'
import fs from 'node:fs'
await pipeline(
fs.createReadStream('./data.csv'),
new CsvToJson(),
fs.createWriteStream('./data.jsonl', { encoding: 'utf-8' })
)// 文件监听
import { watch } from 'node:fs'
const watcher = watch('./src', { recursive: true }, (eventType, filename) => {
console.log(`${eventType}: ${filename}`)
})
// 手动关闭
// watcher.close()调试与性能分析
// 使用内置调试器
// 启动:node --inspect src/index.js
// 然后在 Chrome DevTools 中打开 chrome://inspect
// 性能分析
// node --prof src/index.js
// 生成分析报告:node --prof-process isolate-*.log
// 堆快照 — 排查内存泄漏
// node --inspect src/index.js
// 在 DevTools > Memory > Take Heap Snapshot
// 诊断报告
// node --diagnostic-dir=./reports src/index.js// util 工具模块
import util from 'node:util'
// 带颜色输出
console.log(util.inspect({ a: 1, b: [2, 3] }, { colors: true, depth: null }))
// 将回调函数转为 Promise
import { readFile } from 'node:fs'
const readFileAsync = util.promisify(readFile)
// 文本格式化
const debugLog = util.debuglog('app')
debugLog('这条日志只在 NODE_DEBUG=app 时输出')npm 与 package.json
# 初始化项目
npm init -y
# 安装生产依赖
npm install express
# 安装开发依赖
npm install -D typescript eslint vite
# 查看过时依赖
npm outdated
# 安全审计
npm audit
npm audit fix{
"name": "node-basic-demo",
"version": "1.0.0",
"type": "module",
"scripts": {
"dev": "node --watch src/index.js",
"start": "node src/index.js",
"lint": "eslint .",
"build": "vite build"
},
"dependencies": {
"express": "^4.21.0"
},
"devDependencies": {
"eslint": "^9.0.0",
"vite": "^5.4.0"
},
"engines": {
"node": ">=18.0.0"
}
}# 推荐使用 nvm 管理版本
nvm install 20
nvm use 20
node -v
npm -vNode.js 内置模块速查
// os — 操作系统信息
import os from 'node:os'
console.log('平台:', os.platform()) // 'win32' / 'linux' / 'darwin'
console.log('CPU 核心数:', os.cpus().length)
console.log('总内存:', (os.totalmem() / 1024 / 1024 / 1024).toFixed(2), 'GB')
console.log('空闲内存:', (os.freemem() / 1024 / 1024 / 1024).toFixed(2), 'GB')
console.log('主机名:', os.hostname())
console.log('用户目录:', os.homedir())
// path — 路径处理
import path from 'node:path'
path.join('/foo', 'bar', 'baz') // '/foo/bar/baz'
path.resolve('src', './index.js') // 绝对路径
path.extname('file.ts') // '.ts'
path.basename('/foo/bar/file.ts') // 'file.ts'
path.dirname('/foo/bar/file.ts') // '/foo/bar'
path.parse('/foo/bar/file.ts') // { root, dir, base, ext, name }
// url — URL 解析
import { URL } from 'node:url'
const myUrl = new URL('https://example.com:8080/path?id=1#hash')
console.log(myUrl.hostname) // 'example.com'
console.log(myUrl.port) // '8080'
console.log(myUrl.searchParams.get('id')) // '1'
// crypto — 加密
import crypto from 'node:crypto'
const hash = crypto.createHash('sha256').update('hello').digest('hex')
console.log('SHA256:', hash)
const randomId = crypto.randomBytes(16).toString('hex')
console.log('随机 ID:', randomId)Express 快速入门
// app.mjs — 使用 Express 构建 REST API
import express from 'express'
import cors from 'cors'
const app = express()
// 中间件
app.use(cors())
app.use(express.json()) // 解析 JSON 请求体
app.use(express.urlencoded()) // 解析 URL 编码
// 简单内存数据存储
let users = [
{ id: 1, name: '张三', email: 'zhangsan@example.com' },
{ id: 2, name: '李四', email: 'lisi@example.com' },
]
// RESTful 路由
app.get('/api/users', (req, res) => {
const { keyword, page = 1, pageSize = 10 } = req.query
let filtered = users
if (keyword) {
filtered = users.filter(u =>
u.name.includes(keyword) || u.email.includes(keyword)
)
}
const start = (Number(page) - 1) * Number(pageSize)
const paged = filtered.slice(start, start + Number(pageSize))
res.json({ data: paged, total: filtered.length })
})
app.get('/api/users/:id', (req, res) => {
const user = users.find(u => u.id === Number(req.params.id))
if (!user) return res.status(404).json({ error: '用户不存在' })
res.json(user)
})
app.post('/api/users', (req, res) => {
const { name, email } = req.body
if (!name || !email) {
return res.status(400).json({ error: 'name 和 email 必填' })
}
const newUser = { id: users.length + 1, name, email }
users.push(newUser)
res.status(201).json(newUser)
})
app.put('/api/users/:id', (req, res) => {
const user = users.find(u => u.id === Number(req.params.id))
if (!user) return res.status(404).json({ error: '用户不存在' })
Object.assign(user, req.body)
res.json(user)
})
app.delete('/api/users/:id', (req, res) => {
const index = users.findIndex(u => u.id === Number(req.params.id))
if (index === -1) return res.status(404).json({ error: '用户不存在' })
const deleted = users.splice(index, 1)
res.json(deleted[0])
})
// 错误处理中间件
app.use((err, req, res, next) => {
console.error('服务器错误:', err)
res.status(500).json({ error: '服务器内部错误' })
})
app.listen(3000, () => {
console.log('Express 服务运行在 http://localhost:3000')
})pnpm 与 Monorepo
# pnpm — 更快的包管理器
npm install -g pnpm
pnpm init
# 安装依赖(自动生成 pnpm-lock.yaml)
pnpm add express
pnpm add -D typescript
# Monorepo 示例
mkdir my-monorepo && cd my-monorepo
pnpm init
# pnpm workspace 结构
# packages/
# ├── app/ # 主应用
# ├── ui/ # 组件库
# └── utils/ # 工具库{
"name": "my-monorepo",
"private": true,
"scripts": {
"dev": "pnpm -r --parallel dev",
"build": "pnpm -r build"
}
}// pnpm-workspace.yaml
packages:
- 'packages/*'// packages/ui/package.json — 跨包引用
{
"name": "@myorg/ui",
"dependencies": {
"@myorg/utils": "workspace:*"
}
}优点
缺点
总结
Node.js 基础真正要掌握的是:模块系统、异步 I/O、文件处理、HTTP 服务和 npm 生态。只要把“事件循环 + 异步模型 + 模块加载”这三条主线理解清楚,你就能更好地读懂前端工具链,也能顺利写出基础服务端程序和 CLI 工具。
关键知识点
- Node.js 适合 I/O 密集型,不代表适合所有计算密集型任务。
- ESM 已经是越来越主流的模块方式,但老项目里仍常见 CommonJS。
- Stream 是处理大文件和大流量数据的重要基础能力。
- npm 不只是装包工具,也是工程脚本与依赖治理中心。
项目落地视角
- 前端团队最常用 Node.js 来跑 Vite、Webpack、ESLint、TS 编译等工具。
- 中小型 API 服务也常用 Node.js 搭建轻量后端接口。
- 脚手架、构建脚本、批处理工具非常适合用 Node.js 编写。
- 团队项目必须固定 Node 版本,否则构建结果常不一致。
常见误区
- 认为 Node.js 就是“写后端”,忽略它在工具链中的核心地位。
- 遇到异步问题就到处套 Promise,却没理解事件循环和任务队列。
- 生产环境直接使用开发机上的 Node 版本,没有锁版本策略。
- 依赖装上就用,不做安全审计和许可证检查。
进阶路线
- 学习 Express、Fastify、NestJS 等 Node 服务端框架。
- 研究事件循环、Worker Threads、Cluster 与性能优化。
- 深入理解包发布、Monorepo、pnpm 与构建工具链。
- 进一步学习 SSR、BFF、实时通信和边缘运行时。
适用场景
- 前端构建工具链和脚本系统。
- API 服务、BFF 层、轻量网关。
- CLI 工具、文件处理程序、自动化任务。
- 实时通信、日志处理、流式数据场景。
落地建议
- 团队统一 Node 版本,建议配
.nvmrc或engines字段。 - 新项目优先明确使用 ESM 还是 CommonJS,不要混乱切换。
- 对核心 npm 依赖建立版本锁定和安全审计流程。
- 对大文件处理、日志处理优先考虑 Stream,而不是一次性读入内存。
排错清单
- 依赖装不上时,先检查 Node/npm 版本是否匹配。
- 模块导入异常时,先看
type: module、扩展名和导出方式。 - 服务性能异常时,先判断是 CPU 阻塞还是 I/O 阻塞。
- 线上行为和本地不一致时,先核对运行时版本和环境变量。
复盘问题
- 当前项目里,Node.js 承担的是工具链角色还是服务端角色?
- 你的主要瓶颈来自模块系统、异步逻辑,还是依赖治理?
- 如果今天要把项目迁移到新版本 Node,风险点在哪里?
- 你的脚本和服务是否已经足够适合团队协作和长期维护?
