首页 / 速查手册 / 在线

Node.js速查手册

Node模块/fs/path/Stream/事件循环速查

速查手册 · 1 次 · 2026-10-04 · 分享 · 全屏

📦 模块系统 CommonJS / ESM

CommonJS(require / module.exports)

Node 传统模块系统,动态加载、可在任意位置 require。

// math.js
function add(a, b) { return a + b }
const PI = 3.14
module.exports = { add, PI }
// 或逐个挂载
exports.sub = (a, b) => a - b

// app.js
const math = require('./math')
const { add } = require('./math')
const path = require('node:path')   // 加 node: 前缀更明确
console.log(add(1, 2))
ESM(import / export)

现代标准,静态分析、支持 tree-shaking;文件用 .mjs 或 package.json 声明。

// math.js
export const PI = 3.14
export function add(a, b) { return a + b }
export default { version: '1.0' }

// app.js
import math, { add, PI } from './math.js'
import * as M from './math.js'
import { readFile } from 'node:fs/promises'

console.log(add(1, 2), PI)
开启 ESM 与 __dirname 替代

在 package.json 加 "type": "module";ESM 下没有 __dirname/__filename。

// package.json
{
  "type": "module"
}

// ESM 中模拟 __dirname / __filename
import { fileURLToPath } from 'node:url'
import { dirname, join } from 'node:path'

const __filename = fileURLToPath(import.meta.url)
const __dirname = dirname(__filename)
const cfg = join(__dirname, 'config.json')
动态 import()

返回 Promise,可按条件或运行时路径加载,适合按需加载。

async function load(kind) {
  if (kind === 'json') {
    const data = await import('./data.json', { with: { type: 'json' } })
    return data.default
  }
  const mod = await import(`./plugins/${kind}.js`)
  return mod.default
}

// CommonJS 中也可用于异步加载 ESM
;(async () => {
  const { default: p } = await import('node:path')
})()

📁 fs 文件系统

读文件(Promise API)

fs/promises 是现代推荐写法,配合 await 无回调地狱。

import { readFile, readdir, stat } from 'node:fs/promises'

const text = await readFile('a.txt', 'utf8')
const buf = await readFile('a.bin')        // 不传编码返回 Buffer

for (const name of await readdir('.')) {
  const s = await stat(name)
  console.log(s.isDirectory() ? 'D' : 'F', name, s.size)
}

// 递归列出全部文件
const all = await readdir('src', { recursive: true })
写入与追加

writeFile 默认覆盖;flag: 'a' 追加;mkdir recursive 递归建目录。

import { writeFile, appendFile, mkdir } from 'node:fs/promises'

await writeFile('b.txt', 'hello\n', 'utf8')
await writeFile('b.txt', JSON.stringify({ a: 1 }, null, 2))

await appendFile('log.txt', `${new Date().toISOString()} ok\n`)

await mkdir('out/logs', { recursive: true })
await writeFile('out/data/a.json', '{}', { flag: 'wx' }) // 已存在则报错
stat / 重命名 / 删除 / 拷贝

cp 支持递归复制目录(Node 16.7+);rm 递归删除。

import { stat, rename, unlink, rm, cp } from 'node:fs/promises'

const s = await stat('a.txt')
s.isFile(); s.isDirectory(); s.size; s.mtime

await rename('old.txt', 'new.txt')
await unlink('tmp.txt')
await rm('dist', { recursive: true, force: true })
await cp('assets', 'backup/assets', { recursive: true })

// 判断存在(不推荐 exists,直接 stat/catch)
try { await stat('x') } catch { /* 不存在 */ }
回调与同步 API

Sync 后缀阻塞事件循环,仅适合启动期/脚本,服务中慎用。

import { readFile } from 'node:fs'

readFile('a.txt', 'utf8', (err, data) => {
  if (err) throw err
  console.log(data)
})

import { readFileSync } from 'node:fs'
const cfg = JSON.parse(readFileSync('config.json', 'utf8'))
// 启动时读取配置可以接受
文件描述符与读写位置

open 后通过 FileHandle 做精细控制;记得关闭。

import { open } from 'node:fs/promises'

const fh = await open('a.txt', 'r')
try {
  const buf = Buffer.alloc(100)
  const { bytesRead } = await fh.read(buf, 0, 100, 0)
  console.log('读到', bytesRead, '字节')
} finally {
  await fh.close()
}

// 标志位:r 只读 / r+ 读写 / w 覆盖写 / a 追加 / wx 新建排他

🛣 path 路径

join / resolve / normalize

join 只拼接,resolve 一定转成绝对路径。

import path from 'node:path'

path.join('src', 'img', 'a.png')        // src/img/a.png
path.resolve('src', 'img/a.png')        // /cwd/src/img/a.png
path.resolve('/a', '/b', 'c')           // /b/c
path.normalize('a//b/../c/')            // a/c/
path.relative('/a/b/c', '/a/d')         // ../../d
path.sep                                // / 或 \
取路径各部分

basename 文件名、dirname 目录、extname 扩展名。

const p = '/data/app/index.test.js'
path.basename(p)              // 'index.test.js'
path.basename(p, '.js')       // 'index.test'
path.dirname(p)               // '/data/app'
path.extname(p)               // '.js'

path.parse(p)
// { root: '/', dir: '/data/app', base: 'index.test.js',
//   ext: '.js', name: 'index.test' }
path.format({ dir: '/x', name: 'a', ext: '.txt' })
跨平台路径与 URL

永远用 path API 拼路径,不要手写 / 或 \;ESM 可用 import.meta.url。

// ❌ Windows 会出问题
const bad = __dirname + '/config.json'
// ✅
const good = path.join(__dirname, 'config.json')

// ESM 里配合 fileURLToPath
import { pathToFileURL } from 'node:url'
const u = pathToFileURL('/data/a.txt')   // file:///data/a.txt

🌐 http 原生服务

最小 HTTP 服务

无需框架即可创建服务;每个请求进入同一个回调。

import http from 'node:http'

const server = http.createServer((req, res) => {
  res.writeHead(200, { 'Content-Type': 'application/json; charset=utf-8' })
  res.end(JSON.stringify({ ok: true, path: req.url }))
})

server.listen(3000, () => {
  console.log('listening http://127.0.0.1:3000')
})
路由与请求方法

用 method + url 自行分发;URL 解析用 WHATWG URL。

http.createServer((req, res) => {
  const url = new URL(req.url, `http://${req.headers.host}`)

  if (req.method === 'GET' && url.pathname === '/ping') {
    res.end('pong')
  } else if (req.method === 'GET' && url.pathname === '/hello') {
    res.end(`hello ${url.searchParams.get('name') ?? ''}`)
  } else {
    res.writeHead(404); res.end('Not Found')
  }
})
读取 POST 请求体

req 是流,需要监听 data/end 收集;注意限制大小。

function readBody(req, limit = 1e6) {
  return new Promise((resolve, reject) => {
    let size = 0
    const chunks = []
    req.on('data', c => {
      size += c.length
      if (size > limit) { reject(new Error('body too large')); req.destroy() }
      chunks.push(c)
    })
    req.on('end', () => resolve(Buffer.concat(chunks).toString('utf8')))
    req.on('error', reject)
  })
}

// 路由内:
// const raw = await readBody(req)
// const body = JSON.parse(raw)
HTTP 客户端 fetch

Node 18+ 内置全局 fetch;低版本可用 http.get 或 undici。

const res = await fetch('https://api.example.com/ping')
console.log(res.status, await res.text())

const r = await fetch('https://api.example.com/users', {
  method: 'POST',
  headers: { 'Content-Type': 'application/json' },
  body: JSON.stringify({ name: 'Tom' })
})
const user = await r.json()

// 经典 API
http.get('http://example.com', (resp) => {
  resp.pipe(process.stdout)
})

⚙️ process 进程

argv / env / cwd

argv 前两项固定是 node 与脚本路径;env 读环境变量。

// node app.js 8080 --debug
console.log(process.argv)
// ['/usr/bin/node', '/path/app.js', '8080', '--debug']
const port = Number(process.argv[2]) || 3000

const env = process.env.NODE_ENV || 'development'
const token = process.env.API_TOKEN

process.cwd()          // 当前工作目录(不一定等于脚本目录)
process.hrtime.bigint()
process.uptime()
退出码与信号

优雅退出:先关服务、清资源再 exit;0 成功非 0 异常。

process.on('SIGTERM', async () => {
  console.log('收到 SIGTERM,准备关闭')
  await server.close()
  process.exit(0)
})

process.on('SIGINT', () => {
  console.log('Ctrl+C')
  process.exit(0)
})

process.on('uncaughtException', (err) => {
  console.error('未捕获异常', err)
  process.exit(1)
})
process.on('unhandledRejection', (reason) => console.error(reason))
stdin/stdout 与 nextTick

标准输入输出本身就是流;nextTick 是最高优先级微任务。

process.stdout.write('请输入:')
process.stdin.setEncoding('utf8')
process.stdin.once('data', (line) => {
  console.log('你输入了', line.trim())
})

// 微任务:promise 队列之前
process.nextTick(() => console.log('tick'))
Promise.resolve().then(() => console.log('promise'))
// 输出:tick -> promise

🔁 事件循环与异步顺序

执行顺序口诀

同步代码 → nextTick → Promise 微任务 → timers → poll → check(setImmediate)。

setTimeout(() => console.log('timeout'), 0)
setImmediate(() => console.log('immediate'))
Promise.resolve().then(() => console.log('promise'))
process.nextTick(() => console.log('nextTick'))
console.log('sync')

// 输出顺序:
// sync -> nextTick -> promise -> (timeout/immediate 视轮次)
setImmediate vs setTimeout(0)

I/O 回调内 setImmediate 一定先于 setTimeout;主模块中二者顺序不定。

const fs = require('node:fs')
fs.readFile(__filename, () => {
  setTimeout(() => console.log('timeout'))
  setImmediate(() => console.log('immediate'))
})
// I/O 回调中:immediate 总是先输出

setImmediate(() => console.log('immediate'))
setTimeout(() => console.log('timeout'))
// 主模块中:顺序不保证
Promise 化回调 API

老回调风格可用 node:util 的 promisify 包装。

import { promisify } from 'node:util'
import { readFile } from 'node:fs'

const read = promisify(readFile)
const txt = await read('a.txt', 'utf8')

const sleep = promisify(setTimeout)
await sleep(1000)
console.log('1 秒后')

🧊 Buffer

创建 Buffer

Buffer 是固定长度的二进制数据容器(Uint8Array 子类)。

Buffer.alloc(10)                    // 10 字节,初始化为 0
Buffer.alloc(5, 0xff)              // 全部 0xff
Buffer.from('hello', 'utf8')       // 字符串 -> Buffer
Buffer.from([104, 101, 108])       // 字节数组 -> Buffer
Buffer.from('6869', 'hex')         // 十六进制 -> Buffer
Buffer.byteLength('中文')          // 字节数(utf8 下为 6)
编码转换

支持 base64、hex、latin1 等,常用于编解码小工具。

const b = Buffer.from('hello 世界', 'utf8')
b.toString('base64')      // aGVsbG8g5LiW55WM
b.toString('hex')         // 68656c6c6f...
b.toString('utf8')        // hello 世界

// base64 解码
Buffer.from('aGVsbG8=', 'base64').toString('utf8')  // hello
拼接与切片

concat 合并多块(网络/流场景);slice 共享内存,复制用 subarray+copy。

const merged = Buffer.concat([Buffer.from('a'), Buffer.from('b')])
merged.length
merged[0]                 // 97(按字节索引)

const part = merged.subarray(0, 1)
const dst = Buffer.alloc(2)
merged.copy(dst, 0, 0, 2)
Buffer.compare(a, b)      // 0 表示相等

🌊 Stream 流

pipe 管道拷贝

读流 pipe 到写流,边读边写不占大内存。

import { createReadStream, createWriteStream } from 'node:fs'

const r = createReadStream('big.zip')
const w = createWriteStream('big.copy.zip')

r.pipe(w)
w.on('finish', () => console.log('拷贝完成'))
r.on('error', err => console.error(err))
data / end 事件消费

手动监听分片;背压(write 返回 false)时 pipe 会自动处理。

const stream = createReadStream('access.log', { encoding: 'utf8', highWaterMark: 64 * 1024 })

stream.on('data', (chunk) => {
  console.log('分片', chunk.length)
})
stream.on('end', () => console.log('读完'))
stream.on('error', (e) => console.error(e))
pipeline 与转换流

pipeline 自动传播错误并清理资源;gzip 是典型转换流。

import { pipeline } from 'node:stream/promises'
import { createGzip } from 'node:zlib'

await pipeline(
  createReadStream('a.txt'),
  createGzip(),
  createWriteStream('a.txt.gz')
)

// 自定义 Transform
import { Transform } from 'node:stream'
const upper = new Transform({
  transform(chunk, enc, cb) {
    cb(null, chunk.toString().toUpperCase())
  }
})

🐞 调试与其他

运行参数与调试

--watch 改代码自动重启,--inspect 用 Chrome DevTools 调试。

node app.js
node --watch app.js               # Node 18+ 文件变化自动重启
node -e "console.log(process.version)"
node -r dotenv/config app.js      # 启动时预加载模块
node --inspect-brk app.js         # 首行断住,等待调试器连接
node --inspect=9229 app.js
# 打开 chrome://inspect 或 VS Code attach
常用全局对象

无需 require 即可使用;部分在 ESM 下不存在(__dirname 等)。

console.log / console.error / console.time('k') / console.table(arr)
process                    // 当前进程
globalThis                 // 全局对象
setTimeout / setInterval / setImmediate
URL / URLSearchParams      // WHATWG URL
TextEncoder / TextDecoder  // 编解码
fetch / Request / Response // Node 18+
AbortController            // 取消异步任务
queueMicrotask(() => {})    // 微任务
EventEmitter 自定义事件

大量核心对象(流、server)都继承自它;on 监听、emit 触发。

import { EventEmitter } from 'node:events'

const bus = new EventEmitter()
bus.on('order', (id) => console.log('收到订单', id))
bus.once('init', () => console.log('只触发一次'))

bus.emit('order', 1001)
bus.emit('init')
bus.off('order', handler)
bus.removeAllListeners('order')

// 捕获错误事件,否则抛出会崩进程
bus.on('error', (err) => console.error(err))

📖 使用说明

全程在浏览器本地运行。

操作步骤:

  1. 搜索或浏览分类条目;
  2. 查看语法与示例代码;
  3. 点击复制代码块。

💬 用户评论 (0)

还没有评论,快来抢沙发!

请添加微信联系我