unplugin-offline-iconify
v1.1.0
Published
Bundle only the Iconify icons used by your app, with sprite and Iconify runtime modes.
Maintainers
Readme
unplugin-offline-iconify
按需打包项目实际使用的 Iconify 图标,生产环境无需从 Iconify API 下载图标。支持 SVG sprite 和 Iconify runtime 两种输出协议。
Vite adapter 在 transform(code, id) 阶段复用已经读取的源码,识别静态的 prefix:name,并为每个图标注入可去重的虚拟模块,不再在启动时额外全量读取源码。其他 adapter 因 JSON/SFC loader 顺序不同,当前使用共享扫描器的构建前全扫 fallback,避免向原始非 JS 内容追加 import;不能据此宣称它们已消除全量扫描。
安装
默认的 sprite 模式没有客户端运行时依赖:
pnpm add -D unplugin-offline-iconify只有使用 iconify 模式时才需要安装可选 peer dependency:
pnpm add @iconify/iconify配置
| 参数 | 类型 | 默认值 | 说明 |
| --- | --- | --- | --- |
| mode | 'sprite' \| 'iconify' | 'sprite' | 选择 SVG symbol 或 Iconify addCollection() 协议 |
| cwd | string | 'src' | 扫描根目录,相对于启动命令的工作目录 |
| include | string \| string[] | '**/*.{js,jsx,ts,tsx,vue,json,astro}' | 参与 transform 扫描的文件 |
| exclude | string \| string[] | [] | 额外排除规则,node_modules 始终排除 |
| safelist | string[] | [] | 动态图标白名单,使用完整的 prefix:name |
以下以 Vite 为例。Webpack、Rollup、Rspack、esbuild、Farm、Astro 和 Nuxt 继续使用包内对应 adapter;Astro/Nuxt 选择 Vite builder 时走 Vite transform 路径,当前端到端验证以 Vite 为主。
Vite 版本兼容
- Vite 6:真实 dev transform、生产 build 与 HMR hook 基线。
- Vite 8:真实生产 build 同时覆盖
sprite和iconify;Vite 8 改用 Rolldown/Oxc 后,本插件使用的标准resolveId、load、transform和 HMR API 无需分支实现。 - Vite 8 要求 Node.js 20.19+ 或 22.12+;这是 Vite 自身要求,不是 sprite/iconify mode 的差异。
- peer 范围为
vite >=3 <9。Vite 3–5 保留兼容入口,但当前自动化矩阵直接验证的是 Vite 6 和 Vite 8。
Vite 8 的主要影响在构建器内部:Rollup/esbuild 分工变成 Rolldown/Oxc。图标扫描复用 Vite 传给插件的 code,不调用 transformWithEsbuild,因此不依赖旧的 esbuild 转换 API。
Vite 用户无需安装 esbuild。包中的 esbuild peer 是 optional,仅在显式使用 unplugin-offline-iconify/esbuild adapter 时由该类项目自行提供。
// vite.config.ts
import OfflineIconify from 'unplugin-offline-iconify/vite'
import { defineConfig } from 'vite'
export default defineConfig({
plugins: [
OfflineIconify({
// mode: 'sprite',
safelist: ['mdi:account'],
}),
],
})在应用入口保留一次基础虚拟模块导入。静态图标也会通过 transform 自动注入图标子模块;显式导入可确保仅使用 safelist 时注册器仍会加载。
import 'virtual:offline-iconify'TypeScript 项目可在环境声明中加入:
/// <reference types="unplugin-offline-iconify/client" />Sprite 模式(默认)
默认行为与 1.0.1 兼容:插件把离线图标写入隐藏的 SVG sprite,每个 symbol ID 都是原始的 prefix:name。它不需要 @iconify/iconify。
function Icon(props: { name: string, size?: string }) {
return (
<svg style={{ width: props.size ?? '1em', height: props.size ?? '1em' }}>
<use href={`#${props.name}`} />
</svg>
)
}
export function App() {
return <Icon name="ant-design:account-book-twotone" size="40px" />
}开发时新增、替换或删除图标会沿 Vite 模块图更新;虚拟图标模块带有 dispose 清理逻辑,不会在 sprite 中保留已删除且不再被其他模块引用的 symbol。
Iconify runtime 模式
此模式把同一份离线图标集合交给 @iconify/iconify 的 addCollection(),适合现有代码已经使用 renderSVG()、Iconify DOM 或其他 Iconify API 的项目。
// vite.config.ts
import OfflineIconify from 'unplugin-offline-iconify/vite'
import { defineConfig } from 'vite'
export default defineConfig({
plugins: [
OfflineIconify({
mode: 'iconify',
safelist: ['mdi:account'],
}),
],
})import { renderSVG } from '@iconify/iconify'
import 'virtual:offline-iconify'
const svg = renderSVG('mdi:account')
if (svg)
document.body.appendChild(svg)所有静态识别或 safelist 中的图标数据均来自本地 @iconify/json 并随应用打包,虚拟模块不会请求远程图标。动态图标必须放入 safelist,否则 renderSVG() 无法从离线存储找到它。由于 Iconify runtime 只有追加注册 API,Vite 开发环境在源码变更时使用 full reload 清空旧存储,确保删除图标后状态正确。
从 sprite 迁移到 Iconify runtime
- 安装
@iconify/iconify。 - 将插件配置改为
mode: 'iconify'。 - 保留
import 'virtual:offline-iconify'。 - 把
<use href="#prefix:name">封装替换为现有的renderSVG()或 Iconify 组件/API。 - 将运行期拼接的图标名加入
safelist。
如果项目仍使用 <symbol>/<use>,无需迁移,也无需安装 @iconify/iconify。
静态扫描限制
以下内容可以自动识别并去重:
const icon = 'mdi:home'
const repeated = 'mdi:home'运行时拼接无法静态确定,需要 safelist:
const icon = `mdi:${route.meta.icon}`OfflineIconify({
safelist: ['mdi:home', 'mdi:account'],
})无效的 prefix 或不存在的图标名会被忽略。Iconify alias 会按请求名称解析并固化翻转、旋转等变换,因此两种 mode 使用完全相同的最终图形。
