版本:0.1.7 | 协议:MIT | 依赖:Vite >=5.0.0 <8.0.0
写在前面
v0.1.7 的主题是:让构建产物可追踪,让资源映射可编程。
在 SPA/MPA 应用中,构建产物经过 hash 命名后,原始文件名与实际输出路径之间的映射关系难以获取。v0.1.7 新增的 assetManifest
插件,在构建完成后自动扫描输出目录,生成资源映射清单(manifest.json),支持 Vite 标准、Webpack 兼容和自定义三种输出格式,并可按入口分组或将清单注入为运行时全局变量,实现从构建到运行时的完整资源追踪链路。
本版重点:
| 能力 | 一句话说明 | 你需要做什么 |
|---|---|---|
assetManifest 资源清单生成 |
构建后自动生成资源映射清单,支持三种格式、入口分组和运行时注入 | 新增配置 |
assetManifest 按入口分组 |
将资源按入口点分类,区分 JS/CSS/其他文件 | 新增配置 |
assetManifest 运行时注入 |
将清单以全局变量注入 HTML,运行时可直接访问资源映射 | 新增配置 |
升级方式 :修改 devDependencies 中版本号为 ^0.1.7。无 Breaking Changes,完全向后兼容。
一、5 分钟快速上手
1.1 安装与最小配置
json
{
"devDependencies": {
"@meng-xi/vite-plugin": "^0.1.7"
}
}
typescript
import { assetManifest } from '@meng-xi/vite-plugin'
export default defineConfig({
plugins: [assetManifest()]
})
1.2 立刻看到效果
构建完成后,dist/manifest.json 自动生成:
json
{
"version": "1.0",
"timestamp": "2026-06-07T15:30:00.000Z",
"publicPath": "/",
"assets": {
"index.html": "/index.html",
"assets/index-abc123.js": "/assets/index-abc123.js",
"assets/index-def456.css": "/assets/index-def456.css",
"assets/logo-ghi789.png": "/assets/logo-ghi789.png"
}
}
1.3 运行时访问清单
启用 injectRuntime 后,在浏览器中可直接访问资源映射:
typescript
// vite.config.ts
assetManifest({
injectRuntime: true,
runtimeGlobalName: '__ASSET_MANIFEST__'
})
// 运行时代码
const manifest = window.__ASSET_MANIFEST__
console.log(manifest.assets['assets/logo-ghi789.png']) // '/assets/logo-ghi789.png'
二、核心能力:assetManifest 资源清单生成
2.1 三种输出格式
Vite 标准格式(默认)
键为原始资源路径,值为带 publicPath 的输出路径,并包含版本号和时间戳元信息:
json
{
"version": "1.0",
"timestamp": "2026-06-07T15:30:00.000Z",
"publicPath": "/",
"assets": {
"index.html": "/index.html",
"assets/index-abc123.js": "/assets/index-abc123.js"
}
}
Webpack 兼容格式
模拟 Webpack ManifestPlugin 的输出结构,包含 entries 和 assets 两个字段,方便从 Webpack 迁移的项目使用:
json
{
"entries": [
{
"name": "main",
"files": ["/assets/index-abc123.js", "/assets/index-def456.css"]
}
],
"assets": {
"index.html": "/index.html",
"assets/index-abc123.js": "/assets/index-abc123.js"
}
}
自定义格式
通过 customFormatter 回调函数,完全控制输出结构:
typescript
assetManifest({
outputFormat: 'custom',
customFormatter: assets => {
// 只输出 JS 和 CSS 文件
const filtered = Object.fromEntries(Object.entries(assets).filter(([key]) => key.endsWith('.js') || key.endsWith('.css')))
return { files: filtered, generatedAt: new Date().toISOString() }
}
})
2.2 按入口分组
启用 groupByEntry 后,清单中会包含按入口点分组的资源信息,将每个入口关联的 JS、CSS 和其他资源文件分类整理:
typescript
assetManifest({
outputFormat: 'vite',
groupByEntry: true
})
生成的清单会额外包含 groups 字段:
json
{
"version": "1.0",
"timestamp": "2026-06-07T15:30:00.000Z",
"publicPath": "/",
"assets": { "...": "..." },
"groups": [
{
"entry": "main",
"assets": {
"js": ["/assets/index-abc123.js"],
"css": ["/assets/index-def456.css"],
"other": ["/assets/logo-ghi789.png"]
}
},
{
"entry": "vendor",
"assets": {
"js": ["/assets/vendor-xyz999.js"],
"css": [],
"other": []
}
}
]
}
2.3 运行时注入
启用 injectRuntime 后,插件会在构建完成后将资源映射表以 <script> 标签的形式注入到输出目录中的所有 HTML 文件的 </head> 标签前:
typescript
assetManifest({
injectRuntime: true,
runtimeGlobalName: '__ASSET_MANIFEST__'
})
注入后的 HTML:
html
<head>
<script>
window.__ASSET_MANIFEST__ = { assets: { 'index.html': '/index.html', 'assets/index-abc123.js': '/assets/index-abc123.js' } }
</script>
<!-- 其他 head 内容 -->
</head>
运行时即可通过全局变量访问:
typescript
const manifest = window.__ASSET_MANIFEST__
const jsFiles = Object.keys(manifest.assets).filter(key => key.endsWith('.js'))
2.4 文件过滤
通过 includeExtensions、excludeExtensions 和 excludePaths 灵活控制清单包含的文件:
typescript
assetManifest({
// 只包含 JS 和 CSS 文件
includeExtensions: ['.js', '.css'],
// 排除 source map 和压缩文件(默认行为)
excludeExtensions: ['.map', '.gz', '.br'],
// 排除特定路径
excludePaths: ['assets/icons/', 'assets/sprites/']
})
优先级 :excludePaths > excludeExtensions > includeExtensions。
2.5 公共路径前缀
publicPath 会自动添加到所有资源路径前,适配 CDN 部署场景:
typescript
assetManifest({
publicPath: 'https://cdn.example.com/'
})
输出结果:
json
{
"assets": {
"assets/index-abc123.js": "https://cdn.example.com/assets/index-abc123.js"
}
}
2.6 实例方法
插件实例提供三个方法供外部访问清单数据:
typescript
const plugin = assetManifest({ outputFormat: 'vite', groupByEntry: true })
// 构建完成后...
const assetMap = plugin.pluginInstance?.getAssetMap?.() // 资源映射表
const manifest = plugin.pluginInstance?.getManifest?.() // 完整清单数据
const groups = plugin.pluginInstance?.getGroups?.() // 入口分组信息
2.7 冲突检测
构建资源映射表时,如果出现原始路径冲突(多个文件映射到同一个输出路径),插件会自动输出警告日志,帮助发现潜在的构建配置问题。
三、配置选项
| 选项 | 类型 | 默认值 | 描述 |
|---|---|---|---|
| outputFormat | 'vite' | 'webpack' | 'custom' |
'vite' |
清单输出格式 |
| outputFile | string |
'manifest.json' |
清单输出文件名,相对于构建输出目录 |
| includeExtensions | string[] |
[] |
包含的文件扩展名,为空则包含所有 |
| publicPath | string |
'/' |
公共路径前缀 |
| injectRuntime | boolean |
false |
是否将清单注入为运行时全局变量 |
| runtimeGlobalName | string |
'__ASSET_MANIFEST__' |
运行时全局变量名称 |
| customFormatter | CustomFormatter | null |
null |
自定义格式化器,仅 custom 格式时生效 |
| groupByEntry | boolean |
false |
是否按入口分组资源 |
| excludeExtensions | string[] |
['.map', '.gz', '.br'] |
排除的文件扩展名 |
| excludePaths | string[] |
[] |
排除的路径模式列表 |
继承
BasePluginOptions通用配置:enabled、verbose、errorStrategy。
四、类型导出
| 类型 | 描述 |
|---|---|
AssetManifestOptions |
插件配置选项 |
ManifestOutputFormat |
清单输出格式类型 |
AssetMap |
资源映射表,键为原始路径,值为输出路径 |
AssetGroup |
按入口分组的资源信息 |
AssetManifestResult |
Vite 格式的完整清单数据 |
WebpackEntryAsset |
Webpack 格式的入口资源信息 |
WebpackManifestOutput |
Webpack 格式的完整清单输出 |
CustomFormatter |
自定义格式化器函数类型 |
五、实战场景
5.1 CDN 预加载
typescript
import { defineConfig } from 'vite'
import { assetManifest } from '@meng-xi/vite-plugin'
export default defineConfig({
plugins: [
assetManifest({
outputFormat: 'vite',
publicPath: 'https://cdn.example.com/',
injectRuntime: true,
runtimeGlobalName: '__ASSET_MANIFEST__'
})
]
})
运行时根据清单预加载关键资源:
typescript
const manifest = window.__ASSET_MANIFEST__
Object.values(manifest.assets)
.filter(path => path.endsWith('.js'))
.forEach(path => {
const link = document.createElement('link')
link.rel = 'prefetch'
link.href = path
document.head.appendChild(link)
})
5.2 SSR 资源映射
typescript
import { assetManifest } from '@meng-xi/vite-plugin'
export default defineConfig({
plugins: [
assetManifest({
outputFormat: 'vite',
groupByEntry: true
})
]
})
SSR 服务端读取清单文件,根据入口名获取对应的 JS/CSS 文件路径:
typescript
import manifest from './dist/manifest.json'
function getEntryAssets(entryName: string) {
const group = manifest.groups?.find(g => g.entry === entryName)
return {
scripts: group?.assets.js ?? [],
styles: group?.assets.css ?? []
}
}
5.3 Webpack 迁移兼容
typescript
import { assetManifest } from '@meng-xi/vite-plugin'
export default defineConfig({
plugins: [
assetManifest({
outputFormat: 'webpack'
})
]
})
生成的清单与 Webpack ManifestPlugin 格式兼容,无需修改下游消费代码。
5.4 uni-app 项目
typescript
import { assetManifest } from './uni_modules/vite-plugin/js_sdk/index.mjs'
export default defineConfig({
plugins: [
uni(),
assetManifest({
outputFormat: 'vite',
outputFile: 'manifest.json',
injectRuntime: true,
groupByEntry: true,
enabled: process.env.NODE_ENV === 'production'
})
]
})
六、子路径导出变更
| 子路径 | 变更 | 内容 |
|---|---|---|
@meng-xi/vite-plugin/plugins/asset-manifest |
新增 | assetManifest 函数及所有类型导出 |
七、迁移指南
从 v0.1.6 升级到 v0.1.7
1. 启用资源清单生成(可选)
typescript
import { assetManifest } from '@meng-xi/vite-plugin'
export default defineConfig({
plugins: [assetManifest()]
})
2. 按需配置高级功能
typescript
assetManifest({
outputFormat: 'vite', // 或 'webpack'、'custom'
publicPath: '/', // CDN 场景设为 CDN 域名
injectRuntime: true, // 运行时访问清单
groupByEntry: true, // 按入口分组
excludeExtensions: ['.map', '.gz', '.br'] // 排除不需要的文件
})
3. 子路径独立导入
typescript
import { assetManifest } from '@meng-xi/vite-plugin/plugins/asset-manifest'
import type { AssetManifestOptions, AssetMap } from '@meng-xi/vite-plugin/plugins/asset-manifest'
本文基于 @meng-xi/vite-plugin@0.1.7 版本撰写,所有代码示例均来自实际源码。