更新记录
1.0.0(2026-01-26)
- feat: 纯离线、自包含的 Iconify SVG 渲染(uni-app x:Android/iOS/Harmony)
- feat:
<HansIconify />支持icon/size/width/height/color/fallbackIcon+load/error - feat:
<HansIconifyData />支持直接传IconData渲染 - feat: 提供离线注册 API:
registerIcon/registerCollection/getIconData - fix(android): 按屏幕 density 渲染位图,避免高分屏缩放导致的模糊/视觉差异
- fix: SVG 显式添加 preserveAspectRatio,跨平台表现更一致
- docs: README 补充静态图标生成脚本与用法说明
平台兼容性
uni-app x(4.76)
| Chrome | Safari | Android | iOS | 鸿蒙 | 微信小程序 |
|---|---|---|---|---|---|
| - | - | √ | √ | √ | - |
hans-iconify(uni-app x,离线 Iconify)
一个 纯离线、自包含渲染器 的 Iconify SVG 图标组件,支持两种用法:
- 静态图标组件(推荐,需生成):按需引入
icons/<prefix>/*.uvue(SVG 数据内置在组件里) - 运行时注册:通过
registerIcon/registerCollection注册后,用<HansIconify />按icon="mdi:home"方式渲染
目录
- 平台与版本要求
- 安装
- 快速开始(静态图标组件,推荐)
- 用法一:静态图标组件(编译期固定引用)
- 用法二:运行时注册 +
<HansIconify />(动态 icon) - 进阶:
<HansIconifyData />(直接传 IconData) - API:离线注册
- 常见问题
- 平台注意事项
- 生成更多静态图标(脚本)
- 版权与许可
平台与版本要求
- uni-app x(App):Android ✅ / iOS ✅ / Harmony ✅
- 不支持:uni-app(vue2/vue3)/ uni-app x Web / 小程序
- 建议环境:HBuilderX
4.31+、uni-app x4.76+
安装
插件市场
- 在 HBuilderX 插件市场搜索
hans-iconify并导入到项目 - 导入后目录为:
uni_modules/hans-iconify/ - 用 HBuilderX 重新运行到目标平台(iOS/Harmony 需要重新编译/更新基座)
快速开始(静态图标组件,推荐)
为控制体积,本插件仅内置少量示例图标用于跑通 demo。实际项目中,推荐按需生成并引入 icons/<prefix>/*.uvue:
# 1) 复制下方「生成更多静态图标(脚本)」里的文件到你的项目(例如放到 tools/iconify/)
# 2) 安装依赖并生成到 uni_modules/hans-iconify/icons/<prefix>/
npm --prefix tools/iconify i --no-package-lock
npm --prefix tools/iconify run iconify:popular
你也可以用自己的构建流程生成这些 .uvue 静态图标组件(只要输出结构是 icons/<prefix>/*.uvue 即可)。
<template>
<view class="row">
<MdiHome :size="28" color="#2D2A25" />
<MdiCog :size="28" color="#8A8175" />
</view>
</template>
<script setup lang="uts">
import MdiHome from "@/uni_modules/hans-iconify/icons/mdi/home.uvue"
import MdiCog from "@/uni_modules/hans-iconify/icons/mdi/cog.uvue"
</script>
用法一:静态图标组件(编译期固定引用)
适合“图标集合固定、编译期就能确定引用”的场景:直接导入某个 icon 组件即可,无需运行时注册/查表。
Props(静态图标组件)
| 属性 | 类型 | 默认值 | 必填 | 说明 |
|---|---|---|---|---|
| size | Number | 24 | ❌ | 等宽高尺寸(px) |
| color | String | '' | ❌ | 颜色(Android/iOS 使用 currentColor;Harmony 会展开为固定颜色) |
Events(静态图标组件)
| 事件 | 参数 | 说明 |
|---|---|---|
| load | - | 渲染成功 |
| error | IconifyError | 渲染失败(原生渲染失败等) |
用法二:运行时注册 + <HansIconify />(动态 icon)
<HansIconify /> 不内置任何图标数据:使用 icon="xxx" 方式时,必须先注册对应图标(或使用 fallbackIcon)。
建议把 registerIcon/registerCollection 放到 App.uvue / main.uts 或其他“只会执行一次”的公共模块中,避免每个页面重复注册。
<template>
<HansIconify icon="custom:star" :size="28" color="#2D2A25" />
</template>
<script setup lang="uts">
import HansIconify from "@/uni_modules/hans-iconify/components/hans-iconify/hans-iconify.uvue"
import { registerIcon } from "@/uni_modules/hans-iconify"
registerIcon(
"custom:star",
24,
24,
'<path d="M12 17.27L18.18 21l-1.64-7.03L22 9.24l-7.19-.61L12 2 9.19 8.63 2 9.24l5.46 4.73L5.82 21z"/>'
)
</script>
Props(<HansIconify />)
| 属性 | 类型 | 默认值 | 必填 | 说明 |
|---|---|---|---|---|
| icon | String | - | ✅ | 图标名(例如 mdi:home) |
| size | Number | 24 | ❌ | 等宽高尺寸(px) |
| width | Number | 0 | ❌ | 宽(px,优先级高于 size) |
| height | Number | 0 | ❌ | 高(px,优先级高于 size) |
| color | String | '' | ❌ | 颜色(Android/iOS 使用 currentColor;Harmony 会展开为固定颜色) |
| fallbackIcon | String | '' | ❌ | 找不到 icon 时的 fallback(需要你已注册该 icon) |
Events(<HansIconify />)
| 事件 | 参数 | 说明 |
|---|---|---|
| load | - | 渲染成功 |
| error | IconifyError | 渲染失败(找不到 icon / 原生渲染失败) |
进阶:<HansIconifyData />(直接传 IconData)
当你已经拿到了图标数据(IconData)并且不想走 registry 时,可以直接使用该组件:
<template>
<HansIconifyData :data="data" :size="28" color="#2D2A25" />
</template>
<script setup lang="uts">
import HansIconifyData from "@/uni_modules/hans-iconify/components/hans-iconify-data/hans-iconify-data.uvue"
import { IconData } from "@/uni_modules/hans-iconify/common/icon-data.uts"
const data = new IconData(24, 24, '<path d="M10 20v-6h4v6h5v-8h3L12 3 2 12h3v8z"/>')
</script>
Props(<HansIconifyData />)
| 属性 | 类型 | 默认值 | 必填 | 说明 |
|---|---|---|---|---|
| icon | String | '' | ❌ | 可选的图标名(仅用于 error 回传,便于定位) |
| data | IconData | - | ✅ | 图标数据 |
| size | Number | 24 | ❌ | 等宽高尺寸(px) |
| color | String | '' | ❌ | 颜色(Android/iOS 使用 currentColor;Harmony 会展开为固定颜色) |
Events(<HansIconifyData />)
| 事件 | 参数 | 说明 |
|---|---|---|
| load | - | 渲染成功 |
| error | IconifyError | 渲染失败(原生渲染失败等) |
API:离线注册
推荐从模块入口导入(稳定 API):
registerIcon(name, width, height, body)registerCollection(prefix, icons)getIconData(name)
类型速览
IconifyError:{ message: string; details?: string; icon?: string }IconData:new IconData(width: number, height: number, body: string)IconEntry:new IconEntry(name: string, data: IconData)
registerIcon 说明
name:推荐使用prefix:name(例如mdi:home)width/height:原始 SVG 的 viewBox 尺寸(通常与 Iconify 的width/height一致)body:SVG 内容片段(不包含最外层<svg>),可包含多个<path>/<g>等
registerCollection 示例
<script setup lang="uts">
import { registerCollection } from "@/uni_modules/hans-iconify"
import { IconData } from "@/uni_modules/hans-iconify/common/icon-data.uts"
import { IconEntry } from "@/uni_modules/hans-iconify/common/icon-entry.uts"
registerCollection("custom", [
new IconEntry("home", new IconData(24, 24, '<path d="M10 20v-6h4v6h5v-8h3L12 3 2 12h3v8z"/>')),
])
</script>
常见问题
1) 图标不显示 / Icon not found
- 确认
icon名称完全一致(例如mdi:home) - 使用
<HansIconify />时,先完成registerIcon/registerCollection再渲染;或给fallbackIcon并确保已注册
2) iOS 标准基座下图标空白
参考下方「iOS 注意事项(看不到图标 / 空白)」:iOS 需要重新运行到 iOS 并打自定义调试基座后再验证。
3) color 不生效 / 多色图标
color 主要用于控制 currentColor。如果图标 body 内写死了 fill/stroke 或本身是多色图标,color 可能不会改变所有颜色。
平台注意事项
Android 注意事项(尺寸/清晰度)
Android 端会根据屏幕 density 生成更高分辨率的位图来渲染 SVG,以避免高分屏缩放导致的模糊/视觉差异。
Harmony 注意事项
Harmony 端使用 @ohos/svg(Canvas 渲染)作为 SVG 渲染器:
- 依赖已在
utssdk/app-harmony/config.json声明:@ohos/svg@2.2.2 - 依赖库支持“多数 SVG 1.1 规范”,但仍可能存在少量标签兼容问题(例如
mask)
iOS 注意事项(看不到图标 / 空白)
hans-iconify 在 iOS 端依赖 UTS 原生 + CocoaPods(SVGKit)。如果使用的是 标准基座(Simulator 里仅安装了 io.dcloud.uniappx),可能会出现:
undefined class: UTSSDKModulesHansIconifyHansIconifyViewByJs- 或 “插件不存在或者编译错误”
这时页面上会表现为图标空白。处理方式:
- macOS 上先配置好 Xcode 环境
- 用 HBuilderX 重新运行到 iOS(真机或重新打自定义调试基座)后再验证
生成更多静态图标(脚本)
本插件不内置大量图标数据文件;你可以用下面的离线脚本把 Iconify collection JSON 生成成静态组件:icons/<prefix>/*.uvue。
下面内容是 可复制 的脚本与配置示例(建议放到你的项目根目录 tools/iconify/;如果你放到别的目录,需要自行调整配置里的 emit.uvueDir 路径)。
如果你要生成大量图标,建议把生成目录加入 .gitignore(例如 uni_modules/hans-iconify/icons/**/*.uvue),避免仓库体积膨胀。
1) 添加 tools/iconify/package.json
{
"name": "hans-iconify-tools",
"private": true,
"type": "module",
"scripts": {
"iconify:popular": "node ./iconify-build.mjs --config ./iconify.popular.config.json"
},
"devDependencies": {
"@iconify-json/bi": "*",
"@iconify-json/lucide": "*",
"@iconify-json/mdi": "*",
"@iconify-json/ri": "*",
"@iconify-json/tabler": "*"
}
}
你也可以只安装你需要的 collection:例如只用 MDI 就只装
@iconify-json/mdi。
2) 添加 tools/iconify/iconify.popular.config.json(常用 collections,全量生成,谨慎)
{
"skipMissing": true,
"collections": [
{
"package": "@iconify-json/mdi",
"prefix": "mdi",
"all": true,
"emit": { "uvueDir": "../../uni_modules/hans-iconify/icons/mdi" }
},
{
"package": "@iconify-json/tabler",
"prefix": "tabler",
"all": true,
"emit": { "uvueDir": "../../uni_modules/hans-iconify/icons/tabler" }
},
{
"package": "@iconify-json/lucide",
"prefix": "lucide",
"all": true,
"emit": { "uvueDir": "../../uni_modules/hans-iconify/icons/lucide" }
},
{
"package": "@iconify-json/bi",
"prefix": "bi",
"all": true,
"emit": { "uvueDir": "../../uni_modules/hans-iconify/icons/bi" }
},
{
"package": "@iconify-json/ri",
"prefix": "ri",
"all": true,
"emit": { "uvueDir": "../../uni_modules/hans-iconify/icons/ri" }
}
]
}
试跑/调试(先生成少量看看体积/效果):
npm --prefix tools/iconify i --no-package-lock
npm --prefix tools/iconify run iconify:popular -- --limit 200
3)(可选)添加 tools/iconify/iconify.config.json(按需生成)
{
"package": "@iconify-json/mdi",
"prefix": "mdi",
"icons": ["home", "cog"],
"emit": {
"uvueDir": "../../uni_modules/hans-iconify/icons/mdi"
}
}
4) 添加 tools/iconify/iconify-build.mjs(脚本源码)
#!/usr/bin/env node
import fs from 'node:fs/promises'
import path from 'node:path'
import { createRequire } from 'node:module'
function parseArgs(argv) {
const args = {}
for (let i = 0; i < argv.length; i++) {
const token = argv[i]
if (!token.startsWith('--')) continue
const key = token.slice(2)
const next = argv[i + 1]
const hasValue = next != null && !next.startsWith('--')
args[key] = hasValue ? next : true
if (hasValue) i++
}
return args
}
function usage() {
return [
'Iconify collection JSON -> hans-iconify 静态图标生成器',
'',
'用法:',
' node tools/iconify/iconify-build.mjs --collection /path/to/mdi.json --icons home,cog --out-uvue-dir uni_modules/hans-iconify/icons/mdi',
' node tools/iconify/iconify-build.mjs --package @iconify-json/mdi --icons home,cog --out-uvue-dir uni_modules/hans-iconify/icons/mdi',
' node tools/iconify/iconify-build.mjs --package @iconify-json/mdi --out-uvue-dir uni_modules/hans-iconify/icons/mdi',
' node tools/iconify/iconify-build.mjs --config tools/iconify/iconify.config.json',
'',
'参数:',
' --config <path> 配置文件(JSON)',
' --collection <path> Iconify collection JSON 文件路径(与 --package 二选一;也可在 config 里提供)',
' --package <name> 使用 @iconify-json/<prefix> npm 包作为数据源(与 --collection 二选一;也可在 config 里提供)',
' --prefix <name> collection prefix(默认从 JSON 里读取)',
' --icons <a,b,c> 图标名列表(不带 prefix;默认从 config 里读取)',
' --all 生成该 collection 的全部 icons(默认不含 aliases;会忽略 --icons/config.icons)',
' --include-aliases 与 --all 配合:额外包含 aliases 名称(可能因变换字段被跳过)',
' --limit <n> 与 --all 配合:限制最多生成 n 个(用于试跑/调试)',
' --out-uvue-dir <dir> 生成每个图标的 .uvue 文件目录(可选)',
'',
'配置文件:',
' - 单 collection:{ prefix, icons?, all?, package|collection, emit:{ uvueDir } }(默认 all=true)',
' - 多 collection:{ collections:[{ prefix, icons?, all?, package|collection, emit:{ uvueDir } }], skipMissing?: true }(默认每项 all=true)',
'',
'注意:',
' - 目前仅支持直接 icons 数据;带旋转/翻转等变换的 aliases 默认会跳过(或在 strict 模式下报错)。',
].join('\n')
}
function toSingleQuotedStringLiteral(value) {
return (
"'" +
value
.replaceAll('\\', '\\\\')
.replaceAll("'", "\\'")
.replaceAll('\r', '\\r')
.replaceAll('\n', '\\n') +
"'"
)
}
function isPlainObject(value) {
return value != null && typeof value === 'object' && !Array.isArray(value)
}
function normalizeIconList(iconsValue) {
if (iconsValue == null) return []
if (Array.isArray(iconsValue)) return iconsValue.map((v) => String(v).trim()).filter(Boolean)
return String(iconsValue)
.split(',')
.map((s) => s.trim())
.filter(Boolean)
}
function pickNumber(value, fallback) {
return typeof value === 'number' && Number.isFinite(value) ? value : fallback
}
function resolveIconData(setJson, iconName) {
return resolveIconDataInternal(setJson, iconName, new Set())
}
function resolveIconDataInternal(setJson, iconName, seen) {
if (seen.has(iconName)) {
throw new Error(`Alias loop detected at "${iconName}"`)
}
seen.add(iconName)
const icons = isPlainObject(setJson.icons) ? setJson.icons : null
if (icons != null && isPlainObject(icons[iconName])) {
return icons[iconName]
}
const aliases = isPlainObject(setJson.aliases) ? setJson.aliases : null
if (aliases != null && isPlainObject(aliases[iconName])) {
const alias = aliases[iconName]
const parent = typeof alias.parent === 'string' ? alias.parent : null
if (parent == null) {
throw new Error(`Alias "${iconName}" missing "parent"`)
}
const hasTransform =
alias.rotate != null ||
alias.hFlip != null ||
alias.vFlip != null ||
alias.left != null ||
alias.top != null ||
alias.width != null ||
alias.height != null
if (hasTransform) {
throw new Error(
`Alias "${iconName}" has transform fields; please pick its parent icon "${parent}" instead`
)
}
return resolveIconDataInternal(setJson, parent, seen)
}
return null
}
async function ensureDir(dir) {
await fs.mkdir(dir, { recursive: true })
}
const require = createRequire(import.meta.url)
function loadCollectionFromPackage(packageName) {
// @iconify-json/<prefix> packages ship icons.json at package root
// eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
const json = require(`${packageName}/icons.json`)
return json
}
function safeFileName(name) {
return name.replaceAll(/[^a-zA-Z0-9._-]/g, '-')
}
function renderIconUvue({ fullName, width, height, body }) {
const lines = []
lines.push('<template>')
lines.push('\t<HansIconifyData')
lines.push('\t\t:icon="ICON_NAME"')
lines.push('\t\t:data="DATA"')
lines.push('\t\t:size="size"')
lines.push('\t\t:color="color"')
lines.push('\t\t@load="onLoad"')
lines.push('\t\t@error="onError"')
lines.push('\t/>')
lines.push('</template>')
lines.push('')
lines.push('<script setup lang="uts">')
lines.push('import { IconData } from "@/uni_modules/hans-iconify/common/icon-data.uts"')
lines.push('import { IconifyError } from "@/uni_modules/hans-iconify/utssdk/interface.uts"')
lines.push(
'import HansIconifyData from "@/uni_modules/hans-iconify/components/hans-iconify-data/hans-iconify-data.uvue"'
)
lines.push('')
lines.push(`const ICON_NAME = ${toSingleQuotedStringLiteral(fullName)}`)
lines.push(`const DATA = new IconData(${width}, ${height}, ${toSingleQuotedStringLiteral(body)})`)
lines.push('')
lines.push('const props = defineProps({')
lines.push('\tsize: {')
lines.push('\t\ttype: Number,')
lines.push('\t\tdefault: 24,')
lines.push('\t},')
lines.push('\tcolor: {')
lines.push('\t\ttype: String,')
lines.push("\t\tdefault: '',")
lines.push('\t},')
lines.push('})')
lines.push('')
lines.push("const emit = defineEmits(['load', 'error'])")
lines.push('')
lines.push('const size = computed(() => props.size)')
lines.push('const color = computed(() => props.color)')
lines.push('')
lines.push('function onLoad() {')
lines.push("\temit('load')")
lines.push('}')
lines.push('')
lines.push('function onError(e: IconifyError) {')
lines.push("\temit('error', e)")
lines.push('}')
lines.push('</script>')
lines.push('')
return lines.join('\n')
}
function resolveConfigEmitUvueDir(configBaseDir, emit) {
if (!isPlainObject(emit) || typeof emit.uvueDir !== 'string') return null
const dir = String(emit.uvueDir)
if (path.isAbsolute(dir)) return dir
if (dir.startsWith('./') || dir.startsWith('../')) return path.resolve(configBaseDir, dir)
return path.resolve(process.cwd(), dir)
}
function resolveCollectionJsonFromArgsOrConfig(args, config) {
const collectionFromArgs = typeof args.collection === 'string'
const collectionPathRaw = collectionFromArgs
? String(args.collection)
: config != null && typeof config.collection === 'string'
? String(config.collection)
: null
const packageName =
typeof args.package === 'string'
? String(args.package)
: config != null && typeof config.package === 'string'
? String(config.package)
: null
return { collectionFromArgs, collectionPathRaw, packageName }
}
async function loadCollectionJson({ collectionFromArgs, collectionPathRaw, packageName, configBaseDir }) {
if (collectionPathRaw != null && packageName != null) {
throw new Error('Use either --collection or --package (not both)')
}
if (collectionPathRaw != null) {
const collectionPath = path.isAbsolute(collectionPathRaw)
? collectionPathRaw
: path.resolve(collectionFromArgs ? process.cwd() : configBaseDir, collectionPathRaw)
const rawJson = await fs.readFile(collectionPath, 'utf8')
return JSON.parse(rawJson)
}
if (packageName != null) {
try {
return loadCollectionFromPackage(packageName)
} catch {
throw new Error(
`Failed to load ${packageName}/icons.json. Did you install it? (e.g. npm i -D ${packageName})`
)
}
}
return null
}
function getAllIconNamesFromCollection(setJson, { includeAliases }) {
const names = []
const icons = isPlainObject(setJson.icons) ? setJson.icons : null
if (icons != null) names.push(...Object.keys(icons))
if (includeAliases) {
const aliases = isPlainObject(setJson.aliases) ? setJson.aliases : null
if (aliases != null) names.push(...Object.keys(aliases))
}
return Array.from(new Set(names)).sort()
}
function pickBoolean(value) {
return value === true
}
function pickLimit(value) {
if (typeof value === 'number' && Number.isFinite(value) && value > 0) return Math.floor(value)
if (typeof value === 'string') {
const n = Number.parseInt(value, 10)
if (Number.isFinite(n) && n > 0) return n
}
return null
}
async function generateUvueFromCollection({
setJson,
prefix,
icons,
outUvueDir,
skipMissing,
skipInvalid,
}) {
const defaultWidth = pickNumber(setJson.width, 24)
const defaultHeight = pickNumber(setJson.height, 24)
const entries = []
const missing = []
const invalid = []
for (const iconName of icons) {
let iconData = null
try {
iconData = resolveIconData(setJson, iconName)
} catch (e) {
if (skipInvalid) {
invalid.push(iconName)
continue
}
throw e
}
if (iconData == null) {
missing.push(iconName)
continue
}
const body = typeof iconData.body === 'string' ? iconData.body : null
if (body == null || body.length === 0) {
missing.push(iconName)
continue
}
const width = pickNumber(iconData.width, defaultWidth)
const height = pickNumber(iconData.height, defaultHeight)
entries.push({
iconName,
fullName: `${prefix}:${iconName}`,
width,
height,
body,
})
}
if (missing.length > 0) {
if (skipMissing) {
console.warn(`Warn: missing icons for ${prefix}: ${missing.join(', ')}`)
} else {
throw new Error(`Icon(s) not found in ${prefix}: ${missing.join(', ')}`)
}
}
if (invalid.length > 0) {
if (skipInvalid) {
console.warn(`Warn: skipped invalid alias icons for ${prefix}: ${invalid.join(', ')}`)
} else {
throw new Error(`Invalid alias icon(s) in ${prefix}: ${invalid.join(', ')}`)
}
}
if (outUvueDir == null) return
await ensureDir(outUvueDir)
for (const entry of entries) {
const fileName = `${safeFileName(entry.iconName)}.uvue`
const filePath = path.join(outUvueDir, fileName)
await fs.writeFile(filePath, renderIconUvue(entry), 'utf8')
}
}
async function main() {
const args = parseArgs(process.argv.slice(2))
if (args.help || args.h) {
console.log(usage())
process.exit(0)
}
let config = null
let configBaseDir = process.cwd()
if (typeof args.config === 'string') {
configBaseDir = path.dirname(path.resolve(String(args.config)))
const raw = await fs.readFile(args.config, 'utf8')
config = JSON.parse(raw)
}
const outUvueDir =
typeof args['out-uvue-dir'] === 'string'
? args['out-uvue-dir']
: resolveConfigEmitUvueDir(configBaseDir, config != null ? config.emit : null)
const configSkipMissing = config != null && config.skipMissing === true
const collections = config != null && Array.isArray(config.collections) ? config.collections : null
if (collections != null) {
const globalAll = pickBoolean(args.all)
const globalIncludeAliases = pickBoolean(args['include-aliases'])
const globalLimit = pickLimit(args.limit)
for (const item of collections) {
if (!isPlainObject(item)) continue
const itemPrefix = typeof item.prefix === 'string' ? String(item.prefix) : null
if (itemPrefix == null || itemPrefix.trim().length === 0) {
throw new Error('Invalid collections item: missing prefix')
}
const itemOutDir = resolveConfigEmitUvueDir(configBaseDir, item.emit)
const { collectionFromArgs, collectionPathRaw, packageName } = resolveCollectionJsonFromArgsOrConfig(
{},
item
)
const setJson = await loadCollectionJson({
collectionFromArgs,
collectionPathRaw,
packageName,
configBaseDir,
})
if (setJson == null) throw new Error(`Invalid collections item: missing package/collection for ${itemPrefix}`)
const prefixFromJson = typeof setJson.prefix === 'string' ? setJson.prefix : null
const effectivePrefix = itemPrefix.trim()
if (prefixFromJson != null && prefixFromJson.trim().length > 0 && prefixFromJson != effectivePrefix) {
console.warn(`Warn: prefix mismatch. config=${effectivePrefix} json=${prefixFromJson}`)
}
const itemAll = pickBoolean(item.all) || globalAll
const itemIncludeAliases = pickBoolean(item['include-aliases']) || globalIncludeAliases
const itemLimit = pickLimit(item.limit) ?? globalLimit
const explicitIcons = normalizeIconList(item.icons)
const effectiveAll = itemAll || explicitIcons.length === 0
const itemIcons = effectiveAll
? getAllIconNamesFromCollection(setJson, { includeAliases: itemIncludeAliases })
: explicitIcons
if (itemIcons.length === 0) throw new Error(`Invalid collections item: no icons found for ${effectivePrefix}`)
const effectiveIcons = itemLimit != null ? itemIcons.slice(0, itemLimit) : itemIcons
await generateUvueFromCollection({
setJson,
prefix: effectivePrefix,
icons: effectiveIcons,
outUvueDir: itemOutDir,
skipMissing: configSkipMissing,
skipInvalid: effectiveAll || configSkipMissing,
})
console.log(
`Done. prefix=${effectivePrefix} icons=${effectiveIcons.length}` +
(itemOutDir ? ` uvueDir=${itemOutDir}` : '') +
(effectiveAll ? ' all=true' : '')
)
}
return
}
const { collectionFromArgs, collectionPathRaw, packageName } = resolveCollectionJsonFromArgsOrConfig(args, config)
const setJson = await loadCollectionJson({ collectionFromArgs, collectionPathRaw, packageName, configBaseDir })
if (setJson == null) {
console.error('Missing --collection or --package')
console.error('')
console.error(usage())
process.exit(1)
}
const prefix =
typeof args.prefix === 'string'
? args.prefix
: config != null && typeof config.prefix === 'string'
? config.prefix
: typeof setJson.prefix === 'string'
? setJson.prefix
: null
if (prefix == null || prefix.trim().length === 0) {
throw new Error('Missing prefix (use --prefix or provide "prefix" in collection JSON)')
}
const icons = (() => {
const includeAliases = pickBoolean(args['include-aliases'])
const limit = pickLimit(args.limit)
const all = pickBoolean(args.all) || (config != null && pickBoolean(config.all))
if (all) {
const allIcons = getAllIconNamesFromCollection(setJson, { includeAliases })
return limit != null ? allIcons.slice(0, limit) : allIcons
}
if (typeof args.icons === 'string') return normalizeIconList(args.icons)
if (config != null) {
const configIcons = normalizeIconList(config.icons)
if (configIcons.length > 0) return configIcons
}
const allIcons = getAllIconNamesFromCollection(setJson, { includeAliases })
return limit != null ? allIcons.slice(0, limit) : allIcons
})()
if (icons.length === 0) {
throw new Error('No icons found in collection JSON')
}
await generateUvueFromCollection({
setJson,
prefix: prefix.trim(),
icons,
outUvueDir,
skipMissing: false,
skipInvalid: pickBoolean(args.all) || (config != null && pickBoolean(config.all)),
})
console.log(
`Done. prefix=${prefix.trim()} icons=${icons.length}` + (outUvueDir != null ? ` uvueDir=${outUvueDir}` : '')
)
}
main().catch((err) => {
console.error(err instanceof Error ? err.message : String(err))
process.exit(1)
})
参数说明(完整)
node tools/iconify/iconify-build.mjs --help
--config <path>:配置文件(JSON)--collection <path>:Iconify collection JSON 文件(与--package二选一)--package <name>:使用@iconify-json/<prefix>作为数据源(与--collection二选一)--prefix <name>:collection prefix(默认从 JSON 里读)--icons <a,b,c>:图标名列表(不带 prefix;默认从 config 里读)--all:生成该 collection 的全部 icons(默认不含 aliases;会忽略--icons/config.icons)--include-aliases:与--all配合,额外包含 aliases 名称(带变换的 aliases 可能被跳过/报错)--limit <n>:与--all配合,最多生成 n 个(试跑用)--out-uvue-dir <dir>:输出目录(每个图标一个.uvue文件)
版权与许可
- 本插件只负责“渲染与离线注册”,不提供线上拉取服务,也不默认内置大规模图标数据。
- 不同 Iconify collection(
@iconify-json/*)对应的图标有各自的许可证与署名要求;请在你的产品中按所选图标集的许可条款使用与声明。

收藏人数:
购买普通授权版(
试用
赞赏(0)
下载 226
赞赏 0
下载 13724916
赞赏 1851
赞赏
京公网安备:11010802035340号