【前后端】Node.js 模块大全

用到的全部总结在这里,不定期更新

  • 链接
    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
  • axios 发起 HTTP 请求(支持 Promise)
  • body-parser 解析请求体(Express 中常用)
  • commander
  • inquirer 询问 搭配commander
  • chokidar
  • open
  • chalk
  • socket.io
  • 插件机制

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)  // 'https:'
console.log(myURL.hostname)  // 'example.com'
console.log(myURL.port)      // '8080'
console.log(myURL.pathname)  // '/path'
console.log(myURL.hash)      // '#hash'
console.log(url.search);     // '?a=123&b=456'const params = url.searchParams;//得到URLSearchParams 实例对象// 操作 query 参数
myURL.searchParams.set('a', '100')
console.log(myURL.toString()) // https://example.com:8080/path?a=100&b=2#hash// 遍历 query 参数
for (const [key, value] of myURL.searchParams) {console.log(key, value)
}//正则表达式这种 实际上并不是URL底层原理,因为它覆盖不全、边界出错、可读性差、效率低
const url = 'http://www.example.com/a/index.html?a=123&b=456';
const regex = /^(https?):\/\/([^\/?#]+)(\/[^?#]*)?(\?[^#]*)?(#.*)?$/;//URLSearchParams
//空值和无值的参数都会被解析为 ''
const params = new URLSearchParams('?name=Alice&age=30');
console.log(params.get('name'));      // "Alice" 
//还有getAll
console.log(params.has('age'));       // true
params.set('age', '31');              // 修改 age 参数
params.append('hobby', 'reading');    // 添加 hobby 参数
console.log(params.toString());       // "name=Alice&age=31&hobby=reading"
/*
.delete(key)	删除指定参数
.sort()	按参数名排序
.entries()	获取所有键值对迭代器
.keys() / .values()	获取所有键或值的迭代器
.forEach()	遍历所有参数
*///qs
const qs = require('qs')
// 对象 → query 字符串
const str = qs.stringify({a: 1,b: [2, 3],c: { d: 4 }
})
console.log(str)
// a=1&b[0]=2&b[1]=3&c[d]=4
// 字符串 → 对象
const obj = qs.parse('a=1&b[0]=2&b[1]=3&c[d]=4')
console.log(obj)
// { a: '1', b: ['2', '3'], c: { d: '4' } }// URL: /search?filter[status]=active&filter[tags][]=js&filter[tags][]=vue
app.get('/search', (req, res) => {// 默认 Express 不支持嵌套解析,需要 qs 手动解析const filter = require('qs').parse(req.query)console.log(filter)
})

axios 发起 HTTP 请求(支持 Promise)

在nodejs 和 js 用法基本一样,但有细节差异

项目浏览器环境Node.js 环境
请求底层使用 XMLHttpRequestfetch使用 Node 的 httphttps 模块
Cookie 自动携带默认携带当前域名下的 Cookie需要手动设置 withCredentialsheaders
CORS 跨域限制受浏览器安全策略限制不受限制,可自由请求任何地址
文件上传使用 FormData需使用 form-data 模块或 Buffer
浏览器特性支持可显示加载动画、进度条等需手动实现进度监听逻辑
  • 应用场景
    调用第三方 API:比如天气、支付、地图、AI 接口等
    服务间通信:微服务架构中,一个服务调用另一个服务的接口
    数据同步:定时任务拉取远程数据,写入数据库
    代理转发请求:结合 Express 做 API 网关或中间层
    上传/下载文件:配合 fs 模块处理文件流
    自动化脚本:比如爬虫、批量提交数据、接口测试等
//对比 上JS 下node
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 // 允许携带跨域 Cookie
});axios.get('https://api.example.com/data', {headers: {Cookie: 'sessionId=abc123' // 手动设置 Cookie}
});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) // ❌ 默认是 undefined
})const express = require('express')
const bodyParser = require('body-parser')const app = express()// 解析 application/json 类型的请求体
app.use(bodyParser.json())// 解析 application/x-www-form-urlencoded(表单)类型
app.use(bodyParser.urlencoded({ extended: true }))
//统一就写死成true app.post('/login', (req, res) => {console.log(req.body) // ✅ 正常打印对象res.send('登录成功')
})

commander

npm install commander
  • 作用
    命令行工具构建库
    定义命令行命令(如:init、build)
    解析参数(如:–force、-o output)
    自动生成 --help 帮助文档
    监听不同命令的回调函数
  • 使用
// cli.js
const { Command } = require('commander')
const program = new Command()program.name('mycli').description('一个自定义 CLI 工具').version('1.0.0')// 定义命令 mycli init
program.command('init').description('初始化项目').option('-f, --force', '是否强制初始化').action((options) => {require('./commands/init')(options)console.log('初始化项目,参数:', options)})// 解析命令行参数
program.parse(process.argv)
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)})// export 命令
program.command('export').description('导出 HTML').option('-t, --theme <name>', '使用的主题').action((options) => {require('./commands/export')(options)})program.parse()//默认命令 前提:如前面所示program基本信息和后面命令/参数分开
program.action(() => {console.log('你没有输入命令,显示帮助:')program.help()})
//帮助信息自动生成,不需要手动写//支持传参
program.command('add <name>').description('添加一个组件').action((name) => {console.log('添加组件:', name)}).command('export [file]')
.action((file, options) => {// file 是导出的文件名
})//package.json中要配置好
"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,     // 是否忽略第一次加载时触发的 add 事件usePolling: false,        // 如果 true,强制使用轮询(性能较差,兼容某些平台)interval: 100,            // 轮询间隔(仅当 usePolling 为 true)awaitWriteFinish: {stabilityThreshold: 200,//多久没有变化就认为写入完成pollInterval: 100 //检测写入变化的频率}
})watcher.on('change', (path) => {const content = fs.readFileSync(path, 'utf-8')socket.emit('update', content) // 配合 socket.io 实现热更新
})
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'// 在默认浏览器中打开 URL
await open('https://example.com')
// 打开项目目录
await open('./my-project')
// 打开 Markdown 文件(默认编辑器或预览)
await open('./README.md')const open = require('open')
open('https://example.com')// 打开 VSCode
await open('.', { app: { name: 'code' } })// 打开 Chrome 并传参
await open('https://example.com', {app: {name: 'google chrome',arguments: ['--new-window']}
})

chalk

  • 版本
    从 v5 起,chalk 是 ESM-only(只能用 import),如果你项目是 CommonJS,推荐使用 v4 npm install chalk@4
  • 作用:给 命令行文本添加颜色和样式
  • 使用
// ESM 写法(v5+)
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('下划线绿色'))//变量定义 封装success/error/info
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>//两种创建方法 均基于HTTP
//第一种 下面代码中显式导入并创建http server
//第二种 借助app.listen
const express = require('express');
const { Server } = require('socket.io');const app = express();
const server = app.listen(3000); // 返回的是 HTTP Server 实例
const io = new Server(server);io.on('connection', socket => {console.log('客户端连接成功');
});//创建服务端
// server.js
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');
});
<!-- 客户端代码 -->
<!-- index.html -->
<!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>
  • 热更新
//服务端
// 监听 .md 文件变动后
watcher.on('change', (filePath) => {const content = fs.readFileSync(filePath, 'utf-8')io.emit('update', content)
})//客户端
socket.on('update', (html) => {document.querySelector('#app').innerHTML = html
})// ESM 客户端
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)
})

插件机制

  • 作用:支持用户自定义
  • 核心思路
    “插件机制” = 约定接口 + 自动调用
    只需要:
    允许用户传入插件(数组)
    在渲染时循环调用这些插件(钩子)
    插件只要满足某些方法签名就能工作
  • 定义插件格式
//sample_plugin.js
module.exports = {name: 'my-plugin',// 在渲染前调用,可修改原始内容beforeRender(content, options) {return content},// 渲染为 HTML 后调用,可修改 HTML 字符串afterRender(html, options) {return html}
}
//example
// plugins/copyright-plugin.jsmodule.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)// 插件处理 HTML 内容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
  • 创建插件加载器
//loader.js
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
  • 支持命令行传插件
//修改CLI支持--plugin参数 可多次
.option('--plugin <path...>', '加载插件')//...是commander里的rest参数 
  • 插件注册 API 提供 use() 方法,内部注册插件
    方法封装成类
//lib/pluginManager.js
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') {//封装beforeRender->render->afterRenderconst 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 {// 启动服务器时也用 pluginManager.render()
}
  • 插件系统生命周期钩子 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') {// 调用 init 钩子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()// plugins/dev-logger.js
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
    封装自动加载逻辑
//lib/loadLocalPlugins.jsconst 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//控制加载顺序
//在插件文件中加一个 order 字段
module.exports = {name: 'copyright',order: 10,afterRender(html) {return html + '<footer>© 2025</footer>'}
}
//loadLocalPlugins里排序
pluginList.sort((a, b) => (a.order || 0) - (b.order || 0))

搭配上之前的手动传参,实现自动手动一起加载

  • 插件配置文件 支持读取 .previewrc 配置文件加载插件
    让用户通过项目根目录的 .previewrc 文件自动配置插件和主题,避免复杂命令行参数。
//支持JSON格式
{"theme": "hacker","plugins": ["./plugins/copyright-plugin.js","./plugins/toc-plugin.js"]
}
//配置读取模块 lib/loadConfig.jsconst 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//调用加载配置并合并参数
// 加载 .previewrc
const rc = loadConfig()// 合并 theme 设置(命令行优先生效)
const theme = options.theme || rc.theme || 'default'// 加载插件(来自 CLI 参数 + .previewrc + plugins/ 目录)
const allPlugins = [...(rc.plugins || []),...(options.plugin || [])
]

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。
如若转载,请注明出处:http://www.pswp.cn/diannao/92043.shtml
繁体地址,请注明出处:http://hk.pswp.cn/diannao/92043.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

双8无碳小车“cad【17张】三维图+设计说名书

基于MATLAB的双八无碳小车轨迹仿真及其结构设计 摘 要 本文设计的基于MATLAB的无碳小车来自于全国大学生工程训练能力竞赛&#xff0c;依据绿色环保&#xff0c;设计一种通过重力势能转换成动能来驱动小车行走的装置。通过分析任务要求&#xff0c;本文完成了小车的三维结构设计…

视觉大模型离线部署全流程优化:从微调技术到工程实践

视觉大模型离线部署全流程优化&#xff1a;从微调技术到工程实践 一、视觉大模型离线部署概述 1.1 视觉大模型的应用场景与挑战 视觉大模型在物体检测、图像生成、图像描述等领域展现出强大能力&#xff0c;已成为人工智能领域的研究热点和产业应用焦点(5)。随着技术的发…

Vue中组件的生命周期

组件的生命周期生命周期、生命周期函数、生命周期钩子vue2的生命周期创建&#xff08;创建前的生命周期函数 beforeCreate &#xff0c;创建完毕created&#xff09;挂载&#xff08;挂载前beforeMount&#xff0c;挂载完毕mounted&#xff09;//把组件放在页面中更新&#xff…

securecrt连接服务器报错 Key exchange failed 怎么办

新买了一台阿里云机&#xff0c;用securecrt去连接&#xff0c;如下报错这个错误表明你的 SSH 客户端与服务器之间无法就密钥交换方法和主机密钥算法达成一致&#xff0c;导致连接失败。这通常是由于客户端和服务器支持的加密算法集不匹配造成的。 解决方式 编辑服务器的/etc/s…

用协议分层模型实战:从物理层到应用层的STM32协议栈开发

目录 1. 揭开协议栈的神秘面纱:从STM32到分层思维 STM32的硬件优势 本章实战:点亮物理层的第一步 2. 数据链路层:让STM32学会“打包”和“拆包” 以太网帧的那些事儿 实战:解析以太网帧 3. 网络层:让STM32学会“找路” LwIP的快速上手 实战:实现一个简单的Ping …

微服务基础环境搭建-centos7

文章目录1、安装docker1.1、安装步骤1.2、docker常用命令2、安装Nginx3、Docker安装Mysql4、Docker安装Redis5、安装Nacos5.1、Nacos的作用5.2、单体服务安装6、安装RocketMQ服务6.1 MQ的作用6.2 RocketMQ的基础服务架构6.2、安装RocketMQ服务6.3、安装dashboard面板服务6.4、R…

Netty知识点

一、Netty的零拷贝机制 零拷贝的基本理念&#xff1a;避免在用户态和内核态之间拷贝数据&#xff0c;从而降低 CPU 占用和内存带宽的消耗除了系统层面的零拷贝。 1、FileRegion 接口 FileRegion 是 Netty 提供的用于文件传输的接口&#xff0c;它通过调用操作系统的 sendfile 函…

Kafka的基本使用

目录 认识Kafka 消息队列 消息队列的核心概念 核心价值与解决的问题 Kafka ZooKeeper Kafka的基本使用 环境安装 启动zookeeper 启动Kafka 消息主题 创建主题 查询主题 修改主题 发送数据 命令行操作 JavaAPI操作 消费数据 命令行操作 JavaAPI操作 认识Kafka…

Flink2.0学习笔记:Table API SQL

stevensu1/EC0720 表 API 和 SQL# 表 API 和 SQL——用于统一流和批处理 加工。表 API 是适用于 Java、Scala 和 Python 的语言集成查询 API&#xff0c;它 允许组合来自关系运算符的查询&#xff0c;例如 selection、filter 和 join in 一种非常直观的方式。Flink 的 SQL 支…

【 SpringAI核心特性 | Prompt工程 】

1. Prompt 工程 基本概念&#xff1a;Prompt ؜工程又叫提示‏词工程&#xff0c;简单来说&#xff0c;就是输入‌给 AI 的指令。 比如下面‏这段内容&#xff0c;就是提示词&#xff1a; 请问桂林电子科技大学是一个怎么样的学校&#xff1f;1.1 Prompt分类 在 AI ؜对话中…

windows wsl2-06-docker hello world

hello-world 例子 就像其他任何一门语言一样&#xff0c;我们来体验 docker 的 hello world $ docker run hello-world但是报错 :~$ docker run hello-world Unable to find image hello-world:latest locally docker: Error response from daemon: Get "https://registry…

Python知识点4-嵌套循环break和continue使用死循环

一、循环【重点掌握】 1.嵌套循环类似于嵌套if语句 语法&#xff1a; while 表达式1&#xff1a;while 表达式2&#xff1a;语句# 1. # 循环5次&#xff0c;打印0~4 m 0 while m < 5:print(m)m 1 # 循环3次&#xff0c;打印0~2 n 0 while n < 3:print(n)n 1print(&qu…

将HTML+JS+CSS数独游戏包装为安卓App

HTMLJSCSS制作一个数独游戏-CSDN博客 中开发了一个数独游戏&#xff0c;这个数独游戏提供了一次性回退到指定步骤的辅助功能&#xff0c;在解决复杂数独问题时十分有帮助&#xff0c;可作为玩数独游戏的辅助工具&#xff0c;因此&#xff0c;考虑将它改装成安卓App安装在手机上…

编程语言Java入门——核心技术篇(一)封装、继承和多态

同专栏基础知识篇写在这里&#xff0c;有兴趣的可以去看看&#xff1a; 编程语言Java入门——基础知识篇&#xff08;一&#xff09;-CSDN博客 编程语言Java入门——基础知识篇&#xff08;二&#xff09;-CSDN博客 编程语言Java入门——基础知识篇&#xff08;三&#xff0…

【39】MFC入门到精通——C++ /MFC操作文件行(读取,删除,修改指定行)

文章目录1 通过关键词&#xff0c;读取某一行 &#xff08;3种方法&#xff09;2 删除 指定行3 修改 指定行1 通过关键词&#xff0c;读取某一行 &#xff08;3种方法&#xff09; 通过定位关键词&#xff0c;读取某一行信息,返回CString //通过定位关键词&#xff0c;读取某…

5 种可行的方法:如何将 Redmi 联系人备份到 Mac

将 Redmi 联系人备份到 Mac 是防止因手机损坏、丢失或更换设备而导致数据丢失的重要措施。虽然云服务提供了便利性&#xff0c;但拥有离线备份可以提供额外的安全性&#xff0c;而无需完全依赖互联网。如果您想知道如何将 Redmi 联系人备份到 Mac&#xff0c;本文将为您介绍 5 …

LeRobot 具身智能机械臂 SO-ARM100 从搭建到训练全流程

今天给大家分享一下 LeRobot 具身智能机械臂 SO-ARM100 的完整使用流程&#xff0c;包括设备组装、环境配置、远程控制、数据录制到模型训练的全过程。适合刚入门具身智能的小伙伴参考学习。 一、前期准备与资源获取 在开始之前&#xff0c;我们需要准备好相关的资源和工具&a…

LINUX720 SWAP扩容;新增逻辑卷;逻辑卷扩容;数据库迁移;gdisk

SWAP空间扩展 方法一 增加硬盘或分区扩展 swap -s mkswap /dev/sdd6 blkid /dev/sdd6 swapon /dev/sdd6 swapon -s vim /etc/fstab /dev/sdd6 swap swap defaults 0 0 开机自动扩容 swap -s [rootweb ~]# lsblk NAME MAJ:MIN RM SIZE RO TYPE MOUNTPOINT sd…

Python 进程间通信:TCP安全加密数据传输

最近在写安全方面的程序&#xff0c;有需求&#xff0c;就做了这些TCP加密数据传输类。 utils.safeUtils的内容详见&#xff1a; SafeObj&#xff1a;Python 高安全性加密数据容器类-CSDN博客SafeKey&#xff1a;Python 高安全性加密密码容器类-CSDN博客 如有任何问题或漏洞欢迎…

Windows批量修改文件属性方法

标题使用icacls命令&#xff08;推荐批量操作&#xff09;打开管理员权限的命令提示符&#xff08;CMD&#xff09;执行以下命令&#xff1a;cmd icacls "文件夹路径" /grant 用户名:(OI)(CI)F /T /C 参数说明&#xff1a;(OI)&#xff1a;对象继承 - 适用于文件夹(C…