用到的全部总结在这里,不定期更新
- 链接
node一本通
包括:
express
path
fs/
process/
os/
http/
mysql/mongoose/
express-jwt/jsonwebtoken/
dotenv/
multer/
swagger/
cors/
nodemon (docker篇有)
- 常用模块
- 内置
fs 文件系统操作(读写、重命名等)
path 路径处理(拼接、解析、扩展名等)
http 创建 HTTP 服务或客户端
os 获取操作系统信息(CPU、内存等)
events 事件驱动机制(监听、触发事件).
stream 处理流式数据(文件读写、管道等).
crypto 加密、哈希、签名等安全相关功能.
child_process 创建子进程、执行命令行任务.
util 工具函数(继承、promisify 等).
- 第三方:
express Web 框架,快速构建 API 服务
axios 发起 HTTP 请求(支持 Promise)
dotenv 加载 .env 环境变量配置
chalk 终端输出彩色文本
commander 构建命令行工具
nodemon 自动重启 Node 服务(开发利器)
jsonwebtoken JWT 鉴权与令牌生成
mongoose MongoDB ODM,操作数据库更方便
cors 处理跨域请求
body-parser 解析请求体(Express 中常用).
multer 处理文件上传
socket.io 实现 WebSocket 实时通信
URL / URLSearchParams/ qs
URL / URLSearchParams 是node/浏览器都支持
qs 第三方库,用于处理复杂query(嵌套对象/数组),放入浏览器需要打包(webpack/vite)npm i qs
const myURL = new URL('https://example.com:8080/path?a=123&b=456#hash')
console.log(myURL.protocol)
console.log(myURL.hostname)
console.log(myURL.port)
console.log(myURL.pathname)
console.log(myURL.hash)
console.log(url.search);
const params = url.searchParams;
myURL.searchParams.set('a', '100')
console.log(myURL.toString())
for (const [key, value] of myURL.searchParams) {
console.log(key, value)
}
const url = 'http://www.example.com/a/index.html?a=123&b=456';
const regex = /^(https?):\/\/([^\/?#]+)(\/[^?#]*)?(\?[^#]*)?(#.*)?$/;
const params = new URLSearchParams('?name=Alice&age=30');
console.log(params.get('name'));
console.log(params.has('age'));
params.set('age', '31');
params.append('hobby', 'reading');
console.log(params.toString());
const qs = require('qs')
const str = qs.stringify({
a: 1,
b: [2, 3],
c: { d: 4 }
})
console.log(str)
const obj = qs.parse('a=1&b[0]=2&b[1]=3&c[d]=4')
console.log(obj)
app.get('/search', (req, res) => {
const filter = require('qs').parse(req.query)
console.log(filter)
})
axios 发起 HTTP 请求(支持 Promise)
在nodejs 和 js 用法基本一样,但有细节差异
项目 |
浏览器环境 |
Node.js 环境 |
请求底层 |
使用 XMLHttpRequest 或 fetch |
使用 Node 的 http 或 https 模块 |
Cookie 自动携带 |
默认携带当前域名下的 Cookie |
需要手动设置 withCredentials 或 headers |
CORS 跨域限制 |
受浏览器安全策略限制 |
不受限制,可自由请求任何地址 |
文件上传 |
使用 FormData |
需使用 form-data 模块或 Buffer |
浏览器特性支持 |
可显示加载动画、进度条等 |
需手动实现进度监听逻辑 |
- 应用场景
调用第三方 API:比如天气、支付、地图、AI 接口等
服务间通信:微服务架构中,一个服务调用另一个服务的接口
数据同步:定时任务拉取远程数据,写入数据库
代理转发请求:结合 Express 做 API 网关或中间层
上传/下载文件:配合 fs 模块处理文件流
自动化脚本:比如爬虫、批量提交数据、接口测试等
import axios from 'axios';
axios.get('/api/user')
.then(res => console.log(res.data))
.catch(err => console.error(err));
const axios = require('axios');
axios.get('https://api.example.com/user')
.then(res => console.log(res.data))
.catch(err => console.error(err));
axios.get('https://api.example.com/data', {
withCredentials: true
});
axios.get('https://api.example.com/data', {
headers: {
Cookie: 'sessionId=abc123'
}
});
const formData = new FormData();
formData.append('file', fileInput.files[0]);
axios.post('/upload', formData, {
headers: { 'Content-Type': 'multipart/form-data' }
});
const FormData = require('form-data');
const fs = require('fs');
const formData = new FormData();
formData.append('file', fs.createReadStream('./file.jpg'));
axios.post('https://api.example.com/upload', formData, {
headers: formData.getHeaders()
});
axios.get('/bigfile', {
onDownloadProgress: progressEvent => {
console.log(`下载进度: ${progressEvent.loaded} 字节`);
}
});
axios.get('https://example.com/bigfile', { responseType: 'stream' })
.then(response => {
let total = 0;
response.data.on('data', chunk => {
total += chunk.length;
console.log(`下载进度: ${total} 字节`);
});
});
body-parser 解析请求体(Express 中常用)
- 作用:在 Express 中,默认 无法读取 req.body,需要 body-parser 这个中间件来解析
- 安装
npm i ...
更新:Express 4.16+ 内置了替代方案!从 Express 4.16 起,可以不用单独装 body-parser,直接使用即可,把bodyParser改为express
app.use(express.json())
app.use(express.urlencoded({ extended: true }))
app.post('/login', (req, res) => {
console.log(req.body)
})
const express = require('express')
const bodyParser = require('body-parser')
const app = express()
app.use(bodyParser.json())
app.use(bodyParser.urlencoded({ extended: true }))
app.post('/login', (req, res) => {
console.log(req.body)
res.send('登录成功')
})
commander
npm install commander
- 作用
命令行工具构建库
定义命令行命令(如:init、build)
解析参数(如:–force、-o output)
自动生成 --help 帮助文档
监听不同命令的回调函数
- 使用
const { Command } = require('commander')
const program = new Command()
program
.name('mycli')
.description('一个自定义 CLI 工具')
.version('1.0.0')
program
.command('init')
.description('初始化项目')
.option('-f, --force', '是否强制初始化')
.action((options) => {
require('./commands/init')(options)
console.log('初始化项目,参数:', options)
})
program.parse(process.argv)
program.parse()
program.parse(['node', 'cli.js', 'start', '--debug']);
program
.command('build')
.description('构建项目')
.option('-o, --output <path>', '输出路径', './dist')
.action((options) => {
require('./commands/build')(options)
console.log('构建项目,输出到:', options.output)
})
program
.command('export')
.description('导出 HTML')
.option('-t, --theme <name>', '使用的主题')
.action((options) => {
require('./commands/export')(options)
})
program.parse()
program
.action(() => {
console.log('你没有输入命令,显示帮助:')
program.help()
})
program
.command('add <name>')
.description('添加一个组件')
.action((name) => {
console.log('添加组件:', name)
})
.command('export [file]')
.action((file, options) => {
})
"bin": {
"mycli": "./cli.js"
}
- 应用:Webpack 和 Vite 本质上就是 CLI(命令行工具),但它们远不止于此——它们不仅是命令行工具,更是构建工具、模块打包器、开发服务器、插件平台、模块解析与优化 、编译器集成。
inquirer 询问 搭配commander
在 commander 中本身不支持交互式选择,它主要用于解析命令行参数。但你可以结合另一个库 —— inquirer 来实现命令行中的“询问”和“选择”功能,比如让用户选择一个选项、输入文本、确认操作等。
- 使用
input 文本输入
number 数字输入
confirm 是/否确认
list 单选列表(上下键选择)
rawlist 数字选择(输入数字)
expand 快捷键选择(按字母)
checkbox 多选列表(空格选择)
password 密码输入(隐藏字符)
editor 打开系统编辑器输入内容
validate: 验证用户输入是否合法
filter: 处理用户输入(如转成大写)
when: 条件提问(根据前面回答决定是否提问)
default: 设置默认值
transformer: 美化显示内容
const { Command } = require('commander');
const inquirer = require('inquirer');
const program = new Command();
program
.command('init')
.description('初始化项目')
.action(async () => {
const answers = await inquirer.prompt([
{
type: 'list',
name: 'framework',
message: '请选择你要使用的框架:',
choices: ['React', 'Vue', 'Svelte'],
},
{
type: 'confirm',
name: 'typescript',
message: '是否启用 TypeScript?',
default: true,
},
]);
console.log('你的选择:', answers);
});
program.parse();
chokidar
- 作用
高效、跨平台的 文件监听库,基于底层 fs.watch 和 fs.watchFile,但做了大量兼容性增强和性能优化。
- 使用
事件名 |
触发时机 |
add |
文件被新增 |
addDir |
文件夹被新增 |
change |
文件内容发生变化 |
unlink |
文件被删除 |
unlinkDir |
文件夹被删除 |
ready |
初始扫描完成 |
error |
出现错误 |
const chokidar = require('chokidar')
const watcher = chokidar.watch('src/**/*.md', {
ignored: /(^|[\/\\])\../,
persistent: true
})
watcher
.on('add', path => console.log(`📄 新增文件: ${path}`))
.on('change', path => console.log(`✏️ 文件变动: ${path}`))
.on('unlink', path => console.log(`❌ 删除文件: ${path}`))
const watcher = chokidar.watch('src', {
ignored: /node_modules/,
persistent: true,
ignoreInitial: false,
usePolling: false,
interval: 100,
awaitWriteFinish: {
stabilityThreshold: 200,
pollInterval: 100
}
})
watcher.on('change', (path) => {
const content = fs.readFileSync(path, 'utf-8')
socket.emit('update', content)
})
watcher.on('change', (filePath) => {
const html = render(fs.readFileSync(filePath, 'utf-8'))
io.emit('reload', html)
})
watcher.on('change', (filePath) => {
build(filePath)
})
open
- 版本
open@10+ 默认导出是 ESM(export default),你用 require() 会报错,如果你用的是 type: “module” 或 .mjs,直接 import chalk from 'chalk'
如果项目是 CommonJS,可以使用 open@9
- 作用
Node.js 第三方库,可以帮你 在默认程序中打开 URL、文件、目录、应用程序等。open 会自动判断当前平台(Windows/macOS/Linux)来选择合适的系统调用,无需你手动处理跨平台问题。
常用于 CLI 工具中,比如 vite 启动后自动打开浏览器:就是用 open(‘http://localhost:3000’) 实现的。
- 使用
打开类型 |
示例 |
打开网页 |
open('https://google.com') |
打开本地文件 |
open('README.md') |
打开本地目录 |
open('.') |
打开应用程序 |
open('https://google.com', { app: 'firefox' }) |
选项 |
作用 |
app.name |
指定打开的应用名(如 chrome , firefox , code , sublime ) |
app.arguments |
传给应用的参数(如打开无痕、指定窗口等) |
wait |
是否等待程序退出(默认 false ) |
newInstance |
是否强制新开一个程序实例 |
import open from 'open'
await open('https://example.com')
await open('./my-project')
await open('./README.md')
const open = require('open')
open('https://example.com')
await open('.', { app: { name: 'code' } })
await open('https://example.com', {
app: {
name: 'google chrome',
arguments: ['--new-window']
}
})
chalk
- 版本
从 v5 起,chalk 是 ESM-only(只能用 import),如果你项目是 CommonJS,推荐使用 v4 npm install chalk@4
- 作用:给 命令行文本添加颜色和样式
- 使用
import chalk from 'chalk'
console.log(chalk.green('成功!'))
console.log(chalk.red('错误!'))
console.log(chalk.yellow('警告!'))
console.log(chalk.blue('信息'))
console.log(chalk.bold.red('加粗红色'))
console.log(chalk.bgYellow.black('黑字黄底'))
console.log(chalk.underline.green('下划线绿色'))
const error = chalk.bold.red
const warning = chalk.hex('#FFA500')
console.log(error('出错了!'))
console.log(warning('警告:请检查配置文件'))
const log = console.log
log(chalk.bold.green('✔ 操作成功'))
log(chalk.bold.yellow('⚠ 请检查配置'))
log(chalk.bold.red('✖ 出现错误'))
log.success('成功')
log.error('失败')
log.info('提示')
console.log(`这是一个 ${chalk.green('成功')} 的例子`)
socket.io
- 作用
Node.js 实时通信库,基于 WebSocket 封装,提供客户端-服务端之间的全双工通信能力,支持自动重连、断线重试、事件通信等。
Socket.IO 是建立在 HTTP 或 HTTPS 协议之上的,它需要一个底层的服务器对象来挂载 WebSocket 通道。所以你必须提供一个 HTTP Server 实例。
Socket.IO 必须依附在 HTTP Server 上运行,确实需要一个 HTTP 服务器对象来初始化 Socket.IO,可以手动创建,也可以用 Express 的 app.listen() 返回值来获取。
- 应用:实时聊天、实时数据推送(如股票/价格更新)、热更新、多人协同编辑
- 对比
功能 |
WebSocket 原生 |
Socket.IO |
连接握手 |
手动管理 |
自动重连 |
消息结构 |
字符串 / 二进制 |
事件机制 |
广播支持 |
❌ |
✅ |
命名空间 / 房间 |
❌ |
✅ |
跨平台/浏览器兼容性 |
差 |
好 |
功能 |
服务端(socket.io) |
客户端(socket.io-client) |
连接 |
io.on('connection', cb) |
io() |
发送消息 |
socket.emit('event', data) |
socket.emit('event', data) |
接收消息 |
socket.on('event', cb) |
socket.on('event', cb) |
广播消息 |
io.emit(...) |
N/A(由服务端触发) |
私发消息 |
socket.to(id).emit(...) |
N/A |
断开连接 |
socket.disconnect() |
socket.disconnect() |
npm install socket.io
npm install socket.io-client
或者HTML引入 <script src="/socket.io/socket.io.js"></script>
const express = require('express');
const { Server } = require('socket.io');
const app = express();
const server = app.listen(3000);
const io = new Server(server);
io.on('connection', socket => {
console.log('客户端连接成功');
});
const express = require('express');
const http = require('http');
const { Server } = require('socket.io');
const app = express();
const server = http.createServer(app);
const io = new Server(server, {
cors: {
origin: '*'
}
})
io.on('connection', (socket) => {
console.log('客户端已连接:', socket.id);
socket.on('chat message', (msg) => {
console.log('收到消息:', msg);
io.emit('chat message', msg);
});
socket.on('disconnect', () => {
console.log('客户端断开:', socket.id);
});
});
server.listen(3000, () => {
console.log('服务器运行在 http://localhost:3000');
});
<!DOCTYPE html>
<html>
<head>
<title>Socket.IO 聊天</title>
</head>
<body>
<input id="input" placeholder="输入消息">
<button onclick="send()">发送</button>
<ul id="messages"></ul>
<script src="/socket.io/socket.io.js"></script>
<script>
const socket = io('http://localhost:3000');
socket.on('chat message', (msg) => {
const li = document.createElement('li');
li.textContent = msg;
document.getElementById('messages').appendChild(li);
});
function send() {
const input = document.getElementById('input');
socket.emit('chat message', input.value);
input.value = '';
}
</script>
</body>
</html>
watcher.on('change', (filePath) => {
const content = fs.readFileSync(filePath, 'utf-8')
io.emit('update', content)
})
socket.on('update', (html) => {
document.querySelector('#app').innerHTML = html
})
import { io } from 'socket.io-client'
const socket = io('http://localhost:3000')
socket.emit('hello', 'hi from client')
socket.on('hello', (msg) => {
console.log('服务端说:', msg)
})
插件机制
- 作用:支持用户自定义
- 核心思路
“插件机制” = 约定接口 + 自动调用
只需要:
允许用户传入插件(数组)
在渲染时循环调用这些插件(钩子)
插件只要满足某些方法签名就能工作
- 定义插件格式
module.exports = {
name: 'my-plugin',
beforeRender(content, options) {
return content
},
afterRender(html, options) {
return html
}
}
module.exports = {
name: 'copyright-plugin',
afterRender(html) {
return html + `<footer><p style="text-align:center;color:#aaa;">© hello 2025 </p></footer>`
}
}
- 修改执行文件来支持插件机制,引入插件加载器.js,用它加载所有插件,循环分别在渲染前/渲染后执行对应函数即beforeRender/afterRender
const fs = require('fs')
const path = require('path')
const loadPlugins = require('./plugins')
function operator(filePath, theme = 'default', pluginPaths = []) {
let Content = fs.readFileSync(filePath, 'utf-8')
const plugins = loadPlugins(pluginPaths)
for (const plugin of plugins) {
if (plugin.beforeRender) {
Content = plugin.beforeRender(Content, { theme })
}
}
const renderer = new xx()
let NewContent = renderer.render(Content)
for (const plugin of plugins) {
if (plugin.afterRender) {
NewContent = plugin.afterRender(NewContent, { theme })
}
}
const cssPath = `/styles/${theme}.css`
return `
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8" />
<title>Preview</title>
<link rel="stylesheet" href="${cssPath}">
</head>
<body>
<div class="content">
${NewContent}
</div>
</body>
</html>`
}
module.exports = operator
const path = require('path')
const fs = require('fs')
function loadPlugins(pluginPaths) {
const plugins = []
for (const pluginPath of pluginPaths) {
const abs = path.resolve(process.cwd(), pluginPath)
if (fs.existsSync(abs)) {
const plugin = require(abs)
plugins.push(plugin)
} else {
console.warn(`⚠️ 插件 ${pluginPath} 不存在`)
}
}
return plugins
}
module.exports = loadPlugins
.option('--plugin <path...>', '加载插件')
- 插件注册 API 提供 use() 方法,内部注册插件
方法封装成类
const fs = require('fs')
const path = require('path')
class PluginManager {
constructor() {
this.plugins = []
}
use(plugin) {
if (typeof plugin === 'object') {
this.plugins.push(plugin)
} else {
console.warn(`插件无效:${plugin}`)
}
return this
}
render(filePath, theme = 'default') {
const cssPath = `/styles/${theme}.css`
return `
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8" />
<title>Preview</title>
<link rel="stylesheet" href="${cssPath}">
</head>
<body>
<div class="content">
${htmlContent}
</div>
</body>
</html>`
}
}
module.exports = new PluginManager()
const pluginManager = require('./pluginManager')
pluginManager
.use(require('./plugins/copyright-plugin'))
.use(require('./plugins/toc-plugin'))
const html = pluginManager.render(filePath, theme)
const pluginManager = require('../lib/pluginManager')
if (options.plugin && options.plugin.length) {
for (const pluginPath of options.plugin) {
const abs = path.resolve(process.cwd(), pluginPath)
const plugin = require(abs)
pluginManager.use(plugin)
}
}
if (options.export) {
const html = pluginManager.render(filePath, options.theme)
fs.writeFileSync(options.export, html)
} else {
}
- 插件系统生命周期钩子 init、beforeRender、afterRender、onError
Node.js 本身并没有统一的“插件生命周期钩子”机制,不像 Webpack、Fastify、Nuxt 等框架那样提供标准化的钩子系统。但在 Node.js 的生态中,很多框架和工具都实现了自己的插件机制和生命周期钩子。
把这些插件自带的钩子都补进 pluginManager.render() 中,非常好扩展。
生命周期钩子名 |
触发时机 |
是否异步支持 |
init(options) |
插件初始化时 |
支持 |
beforeRender(content, options) |
渲染前 |
支持 |
afterRender(html, options) |
渲染后 |
支持 |
onError(err) |
渲染出错时 |
支持 |
onFinish() |
渲染流程结束 |
支持 |
use(plugin) {
if (plugin && typeof plugin === 'object') {
if (typeof plugin.init === 'function') {
try {
plugin.init()
} catch (e) {
console.warn(`插件 ${plugin.name} 初始化失败:`, e)
}
}
this.plugins.push(plugin)
}
return this
}
catch (err) {
console.error(`❌ 渲染失败: ${err.message}`)
for (const plugin of this.plugins) {
if (typeof plugin.onError === 'function') {
try {
plugin.onError(err)
} catch (e) {
console.warn(`插件 ${plugin.name} onError 失败:`, e)
}
}
}
}
for (const plugin of this.plugins) {
if (typeof plugin.onFinish === 'function') {
try {
plugin.onFinish()
} catch (e) {
console.warn(`插件 ${plugin.name} onFinish 失败:`, e)
}
}
}
module.exports = new PluginManager()
module.exports = {
name: 'dev-logger',
init() {
console.log('[dev-logger] 插件已加载')
},
beforeRender(content) {
console.log('[dev-logger] 渲染前内容长度:', content.length)
return content
},
afterRender(html) {
console.log('[dev-logger] 渲染后 HTML 长度:', html.length)
return html
},
onError(err) {
console.error('[dev-logger] 渲染异常:', err)
},
onFinish() {
console.log('[dev-logger] 渲染流程完成 🎉')
}
}
- 插件默认目录 自动加载 ./plugins/*.js
封装自动加载逻辑
const path = require('path')
const fs = require('fs')
function loadLocalPlugins() {
const pluginsDir = path.resolve(process.cwd(), 'plugins')
const pluginList = []
if (!fs.existsSync(pluginsDir)) return []
const files = fs.readdirSync(pluginsDir)
for (const file of files) {
const ext = path.extname(file)
if (ext === '.js') {
const fullPath = path.join(pluginsDir, file)
try {
const plugin = require(fullPath)
pluginList.push(plugin)
} catch (e) {
console.warn(`⚠️ 插件加载失败:${file}`, e)
}
}
}
return pluginList
}
module.exports = loadLocalPlugins
module.exports = {
name: 'copyright',
order: 10,
afterRender(html) {
return html + '<footer>© 2025</footer>'
}
}
pluginList.sort((a, b) => (a.order || 0) - (b.order || 0))
搭配上之前的手动传参,实现自动手动一起加载
- 插件配置文件 支持读取 .previewrc 配置文件加载插件
让用户通过项目根目录的 .previewrc 文件自动配置插件和主题,避免复杂命令行参数。
{
"theme": "hacker",
"plugins": [
"./plugins/copyright-plugin.js",
"./plugins/toc-plugin.js"
]
}
const fs = require('fs')
const path = require('path')
function loadConfig() {
const configPath = path.resolve(process.cwd(), '.previewrc')
if (!fs.existsSync(configPath)) return {}
try {
const raw = fs.readFileSync(configPath, 'utf-8')
const config = JSON.parse(raw)
return config
} catch (e) {
console.warn('⚠️ 配置文件 .previewrc 加载失败:', e.message)
return {}
}
}
module.exports = loadConfig
const rc = loadConfig()
const theme = options.theme || rc.theme || 'default'
const allPlugins = [
...(rc.plugins || []),
...(options.plugin || [])
]