Vite插件0.1.3全新发布:产物体积分析利器
版本:0.1.3 | 协议:MIT | 依赖:Vite ^5.0.0 || ^6.0.0 || ^7.0.0
写在前面
v0.1.3 是 @meng-xi/vite-plugin 的又一个重要版本。这个版本的核心变化:
- 第十个插件
bundleAnalyzer— 构建产物体积分析,支持 JSON/HTML 报告、gzip 计算、阈值告警和构建对比 - 两个新工具模块 —
@common/compress(压缩算法)和@common/path(路径处理) - 工具层增强 —
@common/format和@common/fs新增通用函数,消除插件间重复逻辑
如果你正在使用 v0.1.2,升级到 v0.1.3 无需任何修改——所有变更向后兼容。如果你是第一次接触这个库,这篇文章将从构建产物体积分析这个真实痛点出发,展示第十个插件如何帮你量化构建产物,以及新增的通用工具如何让插件开发更高效。
一、v0.1.2 → v0.1.3 迁移指南
1.1 Breaking Changes
无。v0.1.3 完全向后兼容 v0.1.2,所有现有配置无需修改。
1.2 新增能力一览
| 能力 | 说明 | 是否需要额外配置 |
|---|---|---|
bundleAnalyzer 插件 | 构建产物体积分析(JSON/HTML 报告 + 阈值告警) | 需要新增配置 |
@common/compress 模块 | calculateGzipSize gzip 压缩大小计算 | 按需导入 |
@common/path 模块 | isNodeModule 模块来源判断 | 按需导入 |
@common/format 增强 | escapeHtmlAttr、formatFileSize、getExtension | 按需导入 |
@common/fs 增强 | scanDirectory、writeJsonReport + 2 个类型 | 按需导入 |
1.3 版本号升级
{
"devDependencies": {
"@meng-xi/vite-plugin": "^0.1.3"
}
}
二、从问题出发:bundleAnalyzer 解决了什么
痛点:构建产物体积不可知,优化无据可依
场景:你的 Vite 项目构建完成后,dist/ 目录下有几十个 JS chunk、CSS 文件和静态资源。你隐约感觉产物体积偏大,但不知道:
- 哪个 chunk 最大?里面包含哪些模块?
- 哪些模块来自
node_modules?它们占了多少体积? - gzip 压缩后实际传输体积是多少?
- 某个依赖升级后,产物体积是增大了还是减小了?
- 是否有 chunk 超过了合理的体积阈值?
你可能用 webpack-bundle-analyzer 做过分析,但那是 Webpack 生态的。Vite 生态缺乏一个功能完整的构建产物分析工具。
解法:bundleAnalyzer
import { bundleAnalyzer } from '@meng-xi/vite-plugin'
export default defineConfig({
plugins: [
bundleAnalyzer({
outputFormat: 'both',
sizeThreshold: 200,
topModules: 30,
gzipSize: true,
compareWith: 'bundle-analysis-prev.json',
defaultChartType: 'treemap'
})
]
})
工作原理:
Vite 构建完成 (writeBundle, order: 'post')
↓
扫描 dist/ 目录,收集文件信息
↓
分析每个 chunk 的模块组成
↓
计算原始大小 + gzip 压缩大小(level: 9)
↓
生成 Top N 大模块排行
↓
按扩展名统计文件类型分布
↓
检查体积阈值,生成告警
↓
可选:与上次构建报告对比
↓
生成 JSON 报告和/或 HTML 可视化报告
可选:自动打开浏览器
输出分析摘要日志
三种报告格式
| 格式 | 输出文件 | 适用场景 |
|---|---|---|
json | bundle-analysis.json | 程序化处理、CI/CD 集成、自定义分析工具 |
html | bundle-analysis.html | 人工查看、可视化图表、团队分享 |
both | 同时生成两种 | 两者兼顾 |
HTML 报告可视化
HTML 报告内置三种交互式图表视图:
| 视图 | 特点 | 适用场景 |
|---|---|---|
treemap | 矩形树状图,面积表示体积占比 | 快速定位大模块 |
sunburst | 旭日图,层级嵌套展示模块关系 | 理解模块依赖层次 |
list | 列表视图,按体积排序 | 精确查看每个模块的数值 |
阈值告警机制
bundleAnalyzer({
sizeThreshold: 100 // KB
})
- 超过
sizeThreshold的 chunk → 🟡 普通告警 - 超过
2 × sizeThreshold的 chunk → 🔴 严重告警
终端输出示例:
⚠️ [@meng-xi/vite-plugin:bundle-analyzer] 发现 3 个体积告警:
🟡 chunk "vendor" 超过阈值: 156.3KB > 100KB
🔴 chunk "app" 严重超过阈值: 312.5KB > 100KB (2x)
🟡 chunk "utils" 超过阈值: 128.7KB > 100KB
构建对比:量化每次变更的影响
bundleAnalyzer({
compareWith: 'bundle-analysis-prev.json'
})
启用后,插件会加载上次构建的报告,逐模块对比体积变化:
| 趋势 | 含义 |
|---|---|
increased | 体积增大 |
decreased | 体积减小 |
unchanged | 体积不变 |
added | 本次新增的模块 |
removed | 本次移除的模块 |
终端输出示例:
ℹ️ [@meng-xi/vite-plugin:bundle-analyzer] 构建对比: 3 个增大, 2 个减小, 1 个新增, 0 个移除
分析摘要日志
构建完成后自动输出关键指标:
✅ [@meng-xi/vite-plugin:bundle-analyzer] 产物分析完成: 12 个 chunk, 总体积: 1.19MB (gzip: 384.21KB), 分析耗时: 156ms
入口: 2 | 代码块: 7 | 资源: 3
体积 Top 5 模块:
1. 234.57KB (node_modules) node_modules/lodash/lodash.js
2. 156.23KB (source) src/components/Dashboard.vue
3. 89.45KB (node_modules) node_modules/axios/index.js
4. 67.89KB (source) src/utils/api.ts
5. 45.12KB (source) src/views/Home.vue
三、十大内置插件一览
| 插件 | 解决的问题 | 一句话描述 | 引入版本 |
|---|---|---|---|
buildProgress | 构建无进度反馈 | 终端可视化构建进度条 | 0.0.6 |
bundleAnalyzer | 构建产物体积不可知 | 体积分析 + JSON/HTML 报告 + 阈值告警 + 对比 | 0.1.3 |
compressAssets | 构建产物体积大 | gzip / brotli 压缩 + 报告 | 0.1.2 |
copyFile | 静态资源全量复制 | 智能文件复制(增量 + 并发) | 0.0.6 |
faviconManager | 图标管理繁琐 | 图标注入 + 文件复制一体化 | 0.0.9 |
generateRouter | uni-app 路由手动维护 | pages.json 自动生成路由配置 | 0.0.6 |
generateVersion | 版本号管理缺失 | 多格式版本号生成与注入 | 0.0.6 |
htmlInject | HTML 内容注入缺乏统一方案 | 构建时 HTML 内容注入,支持条件/模板/安全过滤 | 0.1.1 |
loadingManager | 白屏体验差 | 全局 Loading 状态管理 | 0.0.9 |
versionUpdateChecker | 用户无法感知版本更新 | 运行时版本更新检测与提示 | 0.1.0 |
四、通用工具层完善:消除重复逻辑
v0.1.3 的一个重要改进是将插件间的重复逻辑提取为通用工具模块。这些工具不仅被内置插件使用,也可以被你直接导入使用。
4.1 为什么要提取?
在 v0.1.2 中,compressAssets 和 bundleAnalyzer 都需要:
- 计算文件的 gzip 压缩大小 → 各自实现了一遍
- 递归扫描目录、收集文件信息 → 各自实现了一遍
- 将字节数格式化为可读的文件大小 → 各自实现了一遍
- 获取文件扩展名 → 各自实现了一遍
- 判断模块是否来自 node_modules → 各自实现了一遍
- 将分析结果写入 JSON 文件 → 各自实现了一遍
v0.1.3 将这些重复逻辑统一提取到 @common 工具层,确保单一实现、多处复用。
4.2 @common/compress — 压缩算法(新增)
import { calculateGzipSize } from '@meng-xi/vite-plugin/common/compress'
const buffer = Buffer.from('some content to compress')
const gzipSize = await calculateGzipSize(buffer)
console.log(`gzip 压缩后: ${gzipSize} 字节`)
const stringData = 'another long string...'
const size = await calculateGzipSize(stringData)
| 函数 | 参数 | 返回 | 说明 |
|---|---|---|---|
calculateGzipSize | data: Buffer | string | Promise<number> | 计算 gzip 压缩后大小,使用 level: 9 压缩 |
设计决策:使用最高压缩级别(level: 9)而非默认的 level: 6,因为分析场景追求的是估算网络传输的最小体积,而非压缩速度。
4.3 @common/path — 路径处理(新增)
import { isNodeModule } from '@meng-xi/vite-plugin/common/path'
isNodeModule('node_modules/lodash/index.js') // true
isNodeModule('src/utils/helper.ts') // false
isNodeModule('\0some-virtual-module') // true — Rollup 内部虚拟模块
isNodeModule('virtual:import-meta-env') // true — 虚拟模块前缀
| 函数 | 参数 | 返回 | 说明 |
|---|---|---|---|
isNodeModule | moduleId: string | boolean | 判断模块是否来自 node_modules,含虚拟模块检测 |
检测规则:
- 路径包含
node_modules→ 第三方依赖 - 以
\0开头 → Rollup 内部虚拟模块(如\0module-id) - 以
virtual:开头 → 虚拟模块前缀(如virtual:import-meta-env)
4.4 @common/format(增强)
新增三个工具函数:
import { escapeHtmlAttr, formatFileSize, getExtension } from '@meng-xi/vite-plugin/common/format'
escapeHtmlAttr('hello "world"') // 'hello "world"'
escapeHtmlAttr('<script>') // '<script>'
formatFileSize(512) // '512B'
formatFileSize(1536) // '1.5KB'
formatFileSize(2461726) // '2.35MB'
getExtension('dist/app.js') // '.js'
getExtension('dist/style.CSS') // '.css'
| 函数 | 参数 | 返回 | 说明 |
|---|---|---|---|
escapeHtmlAttr | str: string | string | 转义 HTML 属性值中的特殊字符,防止 XSS 注入 |
formatFileSize | bytes: number | string | 字节数格式化为可读文件大小(xB / x.xKB / x.xxMB) |
getExtension | filePath: string | string | 获取文件扩展名,返回小写(含点号,如 .js) |
formatFileSize 转换规则:
| 字节范围 | 输出格式 | 示例 |
|---|---|---|
| < 1KB | xB | 512B |
| < 1MB | x.xKB | 1.5KB |
| ≥ 1MB | x.xxMB | 2.35MB |
4.5 @common/fs(增强)
新增两个工具函数和两个类型:
import { scanDirectory, writeJsonReport } from '@meng-xi/vite-plugin/common/fs'
import type { ScannedFile, ScanDirectoryOptions } from '@meng-xi/vite-plugin/common/fs'
const jsFiles = await scanDirectory('dist', { includeExtensions: ['.js'] })
const allFiles = await scanDirectory('dist', {
excludePatterns: ['node_modules', '.map'],
filter: (filePath, ext, size) => size > 1024
})
await writeJsonReport('dist/report.json', { timestamp: Date.now(), stats: [] })
await writeJsonReport('dist/report.json', data, 4)
| 函数 / 类型 | 说明 |
|---|---|
scanDirectory | 递归扫描目录,支持按扩展名、路径模式和自定义过滤函数过滤 |
writeJsonReport | 将数据序列化为 JSON 并写入文件,默认缩进 2 空格 |
ScannedFile | 扫描文件信息接口(filePath、size、extension) |
ScanDirectoryOptions | 目录扫描选项接口(includeExtensions、excludePatterns、filter) |
scanDirectory 过滤优先级:
1. excludePatterns — 排除的路径模式(支持通配符前缀和子串匹配)
2. includeExtensions — 包含的扩展名(列表非空时生效)
3. filter — 自定义过滤函数(最终过滤)
五、详细 API 文档
5.1 通用配置(BasePluginOptions)
所有插件均继承自 BasePluginOptions,拥有以下通用配置:
| 选项 | 类型 | 默认值 | 说明 |
|---|---|---|---|
enabled | boolean | true | 是否启用插件 |
verbose | boolean | true | 是否启用日志输出 |
errorStrategy | 'throw' | 'log' | 'ignore' | 'throw' | 错误处理策略 |
5.2 bundleAnalyzer — 构建产物体积分析
在 Vite 构建(writeBundle)完成后自动扫描输出目录,分析构建产物的体积分布。
| 选项 | 类型 | 默认值 | 说明 |
|---|---|---|---|
outputFormat | 'json' | 'html' | 'both' | 'json' | 报告输出格式 |
outputFile | string | 'bundle-analysis' | 报告输出文件名(不含扩展名) |
openAnalyzer | boolean | false | 是否在生成 HTML 报告后自动打开浏览器 |
sizeThreshold | number | 100 | 体积告警阈值(KB) |
topModules | number | 20 | Top N 大模块排行数量 |
compareWith | string | null | null | 用于对比的历史报告路径 |
gzipSize | boolean | true | 是否计算 gzip 大小 |
excludeNodeModules | boolean | false | 是否排除 node_modules 中的模块 |
excludePatterns | string[] | [] | 需要排除的文件路径模式列表 |
includeExtensions | string[] | [] | 需要包含的文件扩展名列表,为空则包含所有 |
defaultChartType | 'treemap' | 'sunburst' | 'list' | 'treemap' | HTML 报告中图表的默认展示形式 |
导出类型:BundleAnalyzerOptions、BundleAnalysisResult、BundleOutputFormat、ChunkStats、ModuleStats、FileTypeDistribution、SizeWarning、ComparisonDiff
BundleAnalysisResult:
| 属性 | 类型 | 描述 |
|---|---|---|
timestamp | string | 分析时间戳(ISO 格式) |
totalSize | number | 构建产物总大小(字节) |
totalGzipSize | number | gzip 总大小(字节) |
chunks | ChunkStats[] | chunk 统计列表 |
topModules | ModuleStats[] | Top N 大模块 |
fileTypeDistribution | FileTypeDistribution[] | 文件类型分布统计 |
warnings | SizeWarning[] | 体积阈值告警列表 |
comparisonDiffs | ComparisonDiff[] | 构建对比差异列表 |
analysisTime | number | 分析耗时(毫秒) |
ChunkStats:
| 属性 | 类型 | 描述 |
|---|---|---|
name | string | chunk 名称 |
size | number | 原始大小(字节) |
gzipSize | number | gzip 压缩大小 |
modules | ModuleStats[] | 包含的模块列表 |
type | 'entry' | 'chunk' | 'asset' | chunk 类型 |
fileCount | number | 包含的文件数量 |
ModuleStats:
| 属性 | 类型 | 描述 |
|---|---|---|
id | string | 模块标识符 |
size | number | 模块原始大小(字节) |
gzipSize | number | 模块 gzip 压缩后大小(字节) |
chunks | string[] | 所属 chunk 名称列表 |
imports | string[] | 依赖模块 ID 列表 |
isEntry | boolean | 是否为入口模块 |
isNodeModule | boolean | 是否来自 node_modules |
FileTypeDistribution:
| 属性 | 类型 | 描述 |
|---|---|---|
extension | string | 文件扩展名(如 .js) |
count | number | 该类型的文件数量 |
totalSize | number | 该类型的总大小(字节) |
percentage | number | 该类型的总体积占比(0-100) |
SizeWarning:
| 属性 | 类型 | 描述 |
|---|---|---|
level | 'module' | 'chunk' | 告警级别 |
name | string | 告警目标名称 |
sizeKB | number | 实际大小(KB) |
thresholdKB | number | 阈值大小(KB) |
message | string | 告警消息 |
ComparisonDiff:
| 属性 | 类型 | 描述 |
|---|---|---|
name | string | 模块/chunk 名称 |
previousSize | number | 上次构建大小 |
currentSize | number | 本次构建大小 |
diff | number | 体积变化量 |
diffPercentage | number | 变化百分比 |
trend | 'increased' | 'decreased' | 'unchanged' | 'added' | 'removed' | 变化趋势 |
5.3 compressAssets — 构建产物压缩
| 选项 | 类型 | 默认值 | 说明 |
|---|---|---|---|
algorithm | 'gzip' | 'brotli' | 'both' | 'gzip' | 压缩算法 |
threshold | number | 1024 | 最小压缩阈值(字节) |
deleteOriginalFile | boolean | false | 压缩后是否删除原始文件 |
includeExtensions | string[] | ['.js', '.css', '.html', '.svg', '.json', '.xml', '.txt'] | 包含的扩展名 |
excludeExtensions | string[] | [] | 排除的扩展名(优先级高于 include) |
excludePaths | string[] | [] | 排除的路径前缀 |
compressionLevel | number | 9 | gzip 压缩级别(1-9) |
brotliQuality | number | 11 | brotli 质量参数(1-11) |
reportOutput | string | false | 'compress-report.json' | 报告输出路径,false 不生成 |
parallelLimit | number | 10 | 并发压缩的最大文件数 |
导出类型:CompressAssetsOptions、CompressAlgorithm、CompressStats、CompressSummary
5.4 buildProgress — 构建进度条
| 选项 | 类型 | 默认值 | 说明 |
|---|---|---|---|
width | number | 30 | 进度条宽度(字符数) |
format | 'bar' | 'spinner' | 'minimal' | 'bar' | 显示格式 |
completeChar | string | '█' | 已完成部分填充字符 |
incompleteChar | string | '░' | 未完成部分填充字符 |
clearOnComplete | boolean | true | 完成后是否清除进度条 |
showModuleName | boolean | true | 是否显示当前模块名称 |
theme | ProgressTheme | - | 自定义颜色主题 |
导出类型:BuildProgressOptions、ProgressFormat、BuildPhase、ProgressTheme
5.5 copyFile — 文件复制
| 选项 | 类型 | 默认值 | 说明 |
|---|---|---|---|
sourceDir | string | - | 源目录路径(必填) |
targetDir | string | - | 目标目录路径(必填) |
overwrite | boolean | true | 是否覆盖同名文件 |
recursive | boolean | true | 是否递归复制子目录 |
incremental | boolean | true | 是否启用增量复制 |
导出类型:CopyFileOptions
5.6 faviconManager — 图标管理
| 选项 | 类型 | 默认值 | 说明 |
|---|---|---|---|
base | string | - | 图标文件基础路径 |
url | string | - | 图标完整 URL |
link | string | - | 自定义完整 link 标签 |
icons | HtmlTagDescriptor[] | - | 自定义图标数组 |
copyOptions | object | - | 图标文件复制配置 |
导出类型:FaviconManagerOptions
5.7 generateRouter — 路由配置生成
| 选项 | 类型 | 默认值 | 说明 |
|---|---|---|---|
pagesJsonPath | string | 'src/pages.json' | pages.json 文件路径 |
outputPath | string | 'src/router.config.ts' | 输出文件路径 |
outputFormat | 'ts' | 'js' | 'ts' | 输出文件格式 |
nameStrategy | 'path' | 'camelCase' | 'pascalCase' | 'custom' | 'camelCase' | 路由名称策略 |
customNameGenerator | (path: string) => string | - | 自定义名称生成函数 |
includeSubPackages | boolean | true | 是否包含子包路由 |
watch | boolean | true | 是否监听 pages.json 变化 |
metaMapping | Record<string, string> | {...} | 页面 style 到 meta 的映射 |
exportTypes | boolean | true | 是否导出类型定义 |
preserveRouteChanges | boolean | true | 是否保留用户修改 |
导出类型:GenerateRouterOptions、RouteConfig、RouteMeta、UniAppPagesJson、UniAppPageConfig、UniAppTabBarConfig、OutputFormat、NameStrategy
5.8 generateVersion — 版本号生成
| 选项 | 类型 | 默认值 | 说明 |
|---|---|---|---|
format | 'timestamp' | 'date' | 'datetime' | 'semver' | 'hash' | 'custom' | 'timestamp' | 版本号格式 |
customFormat | string | - | 自定义格式模板 |
semverBase | string | '1.0.0' | 语义化版本基础值 |
outputType | 'file' | 'define' | 'both' | 'file' | 输出类型 |
outputFile | string | 'version.json' | 输出文件路径 |
defineName | string | '__APP_VERSION__' | 注入的全局变量名 |
hashLength | number | 8 | 哈希长度(1-32) |
prefix | string | - | 版本号前缀 |
suffix | string | - | 版本号后缀 |
extra | Record<string, any> | - | 附加信息 |
导出类型:GenerateVersionOptions
5.9 htmlInject — HTML 内容注入
| 选项 | 类型 | 默认值 | 说明 |
|---|---|---|---|
rules | HtmlInjectRule[] | - | 注入规则(必填) |
targetFile | string | 'index.html' | 目标文件匹配 |
security | SecurityConfig | - | 安全过滤配置 |
templateVars | Record<string, string> | - | 全局模板变量 |
logInjection | boolean | false | 是否记录注入日志 |
导出类型:HtmlInjectOptions、HtmlInjectRule、InjectPosition、InjectCondition、SecurityConfig、SelectorMatch
5.10 loadingManager — Loading 状态管理
| 选项 | 类型 | 默认值 | 说明 |
|---|---|---|---|
defaultVisible | boolean | true | 默认是否可见 |
autoHideOn | string | 'DOMContentLoaded' | 自动隐藏时机 |
spinnerType | string | 'circle' | Spinner 类型 |
globalName | string | '__LOADING_MANAGER__' | 全局变量名 |
导出类型:LoadingManagerOptions
5.11 versionUpdateChecker — 版本更新检测
| 选项 | 类型 | 默认值 | 说明 |
|---|---|---|---|
versionSource | 'define' | 'file' | 'auto' | 'auto' | 版本来源 |
checkInterval | number | 300000 | 检查间隔(毫秒) |
promptStyle | 'modal' | 'banner' | 'toast' | 'modal' | 提示样式 |
checkOnVisibilityChange | boolean | true | 标签页切回时是否检查 |
enableInDev | boolean | false | 开发环境是否启用 |
导出类型:VersionUpdateCheckerOptions
六、bundleAnalyzer + compressAssets 联合实战
这两个插件天然互补:bundleAnalyzer 帮你发现体积问题,compressAssets 帮你解决传输问题。
6.1 完整配置
import { defineConfig } from 'vite'
import { bundleAnalyzer, compressAssets, buildProgress, generateVersion } from '@meng-xi/vite-plugin'
export default defineConfig({
plugins: [
buildProgress({ format: 'bar' }),
generateVersion({ format: 'datetime', outputType: 'both' }),
bundleAnalyzer({
outputFormat: 'both',
outputFile: 'bundle-analysis',
sizeThreshold: 200,
topModules: 30,
gzipSize: true,
compareWith: 'bundle-analysis-prev.json',
defaultChartType: 'treemap'
}),
compressAssets({
algorithm: 'both',
threshold: 1024,
reportOutput: 'compress-report.json',
parallelLimit: 10
})
]
})
6.2 CI/CD 集成:体积回归检测
import { bundleAnalyzer } from '@meng-xi/vite-plugin'
export default defineConfig({
plugins: [
bundleAnalyzer({
outputFormat: 'json',
sizeThreshold: 100,
gzipSize: true,
compareWith: 'bundle-analysis-prev.json'
})
]
})
在 CI 脚本中:
# 构建
npm run build
# 检查是否有体积告警(从 JSON 报告中提取 warnings 数组)
node -e "
const report = require('./dist/bundle-analysis.json');
if (report.warnings.length > 0) {
console.error('体积告警:', report.warnings.map(w => w.message).join('; '));
process.exit(1);
}
"
6.3 uni-app 条件启用
import { defineConfig } from 'vite'
import uni from '@dcloudio/vite-plugin-uni'
import { bundleAnalyzer, compressAssets } from './uni_modules/vite-plugin/js_sdk/index.mjs'
export default defineConfig({
plugins: [
uni(),
bundleAnalyzer({
outputFormat: 'json',
sizeThreshold: 100,
gzipSize: true,
enabled: process.env.UNI_PLATFORM === 'h5' && process.env.VITE_USER_NODE_ENV === 'production'
}),
compressAssets({
algorithm: 'both',
threshold: 1024,
reportOutput: 'compress-report.json',
enabled: process.env.UNI_PLATFORM === 'h5' && process.env.VITE_USER_NODE_ENV === 'production'
})
]
})
七、通用工具模块速查
7.1 导入方式
// 全量导入
import { calculateGzipSize, isNodeModule, formatFileSize, scanDirectory } from '@meng-xi/vite-plugin/common'
// 按模块导入
import { calculateGzipSize } from '@meng-xi/vite-plugin/common/compress'
import { isNodeModule } from '@meng-xi/vite-plugin/common/path'
import { escapeHtmlAttr, formatFileSize, getExtension } from '@meng-xi/vite-plugin/common/format'
import { scanDirectory, writeJsonReport } from '@meng-xi/vite-plugin/common/fs'
7.2 完整子路径导出映射
| 入口路径 | 导出内容 |
|---|---|
@meng-xi/vite-plugin | 全量导出(框架 + 插件 + 工具) |
@meng-xi/vite-plugin/plugins | 10 个内置插件工厂函数 + 类型 |
@meng-xi/vite-plugin/plugins/build-progress | buildProgress + 类型 |
@meng-xi/vite-plugin/plugins/bundle-analyzer | bundleAnalyzer + 类型 |
@meng-xi/vite-plugin/plugins/compress-assets | compressAssets + 类型 |
@meng-xi/vite-plugin/plugins/copy-file | copyFile + 类型 |
@meng-xi/vite-plugin/plugins/favicon-manager | faviconManager + 类型 |
@meng-xi/vite-plugin/plugins/generate-router | generateRouter + 类型 |
@meng-xi/vite-plugin/plugins/generate-version | generateVersion + 类型 |
@meng-xi/vite-plugin/plugins/html-inject | htmlInject + 类型 |
@meng-xi/vite-plugin/plugins/loading-manager | loadingManager + 类型 |
@meng-xi/vite-plugin/plugins/version-update-checker | versionUpdateChecker + 类型 |
@meng-xi/vite-plugin/factory | BasePlugin、createPluginFactory、PluginWithInstance |
@meng-xi/vite-plugin/logger | Logger |
@meng-xi/vite-plugin/common | 全部公共工具 |
@meng-xi/vite-plugin/common/compress | calculateGzipSize |
@meng-xi/vite-plugin/common/format | 日期格式化、模板解析、命名转换、文件大小格式化 |
@meng-xi/vite-plugin/common/fs | 文件读写、复制、目录扫描、JSON 报告、并发控制 |
@meng-xi/vite-plugin/common/html | HTML 注入工具 |
@meng-xi/vite-plugin/common/object | deepMerge |
@meng-xi/vite-plugin/common/path | isNodeModule |
@meng-xi/vite-plugin/common/script | 回调包装、XSS 检测、标识符验证 |
@meng-xi/vite-plugin/common/validation | Validator + 验证工具函数 |
八、自定义插件开发:复用通用工具
v0.1.3 新增的通用工具模块不仅服务于内置插件,你也可以在自定义插件中直接使用,避免重复造轮子。
8.1 示例:自定义产物分析插件
import { BasePlugin, createPluginFactory } from '@meng-xi/vite-plugin/factory'
import { scanDirectory, writeJsonReport } from '@meng-xi/vite-plugin/common/fs'
import { calculateGzipSize } from '@meng-xi/vite-plugin/common/compress'
import { formatFileSize, getExtension } from '@meng-xi/vite-plugin/common/format'
import { isNodeModule } from '@meng-xi/vite-plugin/common/path'
import type { Plugin } from 'vite'
interface AssetStatsOptions {
outputFile?: string
excludePatterns?: string[]
}
class AssetStatsPlugin extends BasePlugin<AssetStatsOptions> {
protected getPluginName() {
return 'asset-stats'
}
protected getDefaultOptions() {
return { outputFile: 'asset-stats.json', excludePatterns: [] }
}
protected addPluginHooks(plugin: Plugin): void {
plugin.writeBundle = {
order: 'post',
handler: async () => {
const outDir = this.viteConfig?.build.outDir
if (!outDir) return
const files = await scanDirectory(outDir, {
excludePatterns: this.options.excludePatterns
})
const stats = []
for (const file of files) {
const gzipSize = await calculateGzipSize(file.filePath)
stats.push({
path: file.filePath,
extension: getExtension(file.filePath),
size: formatFileSize(file.size),
gzipSize: formatFileSize(gzipSize),
isNodeModule: isNodeModule(file.filePath)
})
}
await writeJsonReport(this.options.outputFile!, { stats })
this.logger.success(`产物统计完成: ${files.length} 个文件`)
}
}
}
}
export const assetStats = createPluginFactory(AssetStatsPlugin)
8.2 你自动获得的能力
| 工具 | 来源 | 避免的重复实现 |
|---|---|---|
scanDirectory | @common/fs | 递归目录扫描 + 文件过滤 |
calculateGzipSize | @common/compress | gzip 压缩流处理 |
formatFileSize | @common/format | 字节数格式化逻辑 |
getExtension | @common/format | 扩展名提取 + 小写转换 |
isNodeModule | @common/path | node_modules + 虚拟模块检测 |
writeJsonReport | @common/fs | JSON 序列化 + 文件写入 + 错误处理 |
九、架构演进:从 v0.1.2 到 v0.1.3
9.1 v0.1.2 的架构问题
v0.1.2 的九个插件中,compressAssets 内部实现了 gzip 压缩计算、目录扫描、JSON 报告生成等逻辑,这些逻辑在其他场景中也需要使用,但无法复用。
9.2 v0.1.3 的架构改进
v0.1.2 v0.1.3
┌──────────────────────────┐ ┌──────────────────────────────────┐
│ 9 个内置插件 │ │ 10 个内置插件 │
│ + bundleAnalyzer(新增) │ ──→ │ + bundleAnalyzer │
│ 各自实现压缩/扫描/格式化 │ │ 复用 @common 工具层 │
├──────────────────────────┤ ├──────────────────────────────────┤
│ 框架层 │ │ 框架层 │
│ BasePlugin · Validator │ │ BasePlugin · Validator · Logger │
├──────────────────────────┤ ├──────────────────────────────────┤
│ 工具层(6 个模块) │ │ 工具层(8 个模块) │
│ fs · format · html │ │ + compress(新增) │
│ object · script │ │ + path(新增) │
│ validation │ │ fs · format · html · object │
│ │ │ script · validation │
└──────────────────────────┘ └──────────────────────────────────┘
9.3 工具层完整矩阵
| 模块 | 函数/类型 | 使用者 |
|---|---|---|
common/compress | calculateGzipSize | bundleAnalyzer、compressAssets |
common/path | isNodeModule | bundleAnalyzer |
common/format | formatFileSize、getExtension、escapeHtmlAttr | bundleAnalyzer、compressAssets |
common/fs | scanDirectory、writeJsonReport | bundleAnalyzer、compressAssets |
common/html | injectBeforeTag、injectHeadAndBody 等 | faviconManager、loadingManager、versionUpdateChecker |
common/object | deepMerge | BasePlugin |
common/script | makeCallback、containsScriptTag、validateIdentifierName | versionUpdateChecker、loadingManager |
common/validation | Validator + 验证工具函数 | 所有插件 |
十、路线图
短期
bundleAnalyzer支持 Webpack 兼容模式- 构建产物趋势图(多版本体积变化折线图)
- 插件配置预设(
web-app、uni-app、ssr)
中期
- 插件间事件总线
- 可视化配置生成器
- 社区插件市场
长期
成为 Vite 插件开发的标准框架——定义最佳实践,让社区以统一方式构建、分享和组合插件。
本文基于 @meng-xi/vite-plugin@0.1.3 版本撰写,所有代码示例均来自实际源码。
更多推荐

所有评论(0)