【前后端】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
英文地址,請注明出處:http://en.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…