tenon-naive-iconify-picker
| Install | |
|---|---|
npm install tenon-naive-iconify-picker |
|
| Latest Version: | 0.1.3 |
| Vue: | 3 |
| License: | MIT |
| Last Updated: | Jul 13, 2026 |
| Links: | GitHub · npm |
tenon-naive-iconify-picker
English · 简体中文
面向 Vue 3 + Naive UI 的离线优先图标选择器,基于 Iconify。 可注册任意多个图标库(每个库一个 Tab),零网络请求离线浏览,可注入你自己的 SVG,选中结果就是一个字符串 —— 非常适合"给这个菜单选个图标"这类字段。
| 亮色 — 每个注册的库是一个 Tab,离线渲染 | 暗色 — 颜色跟随你的 Naive UI 主题 |
| 切换图标库 — Lucide、Ant Design、Element Plus、Phosphor… | 本地 SVG — 你自己项目的图标 |
特性
- 🧭 多图标库 —— 可注册任意多个图标库(Lucide、Ant Design、Element Plus、Phosphor…),每个库一个 Tab,用户从自己熟悉的库里选。
- 🔌 离线 —— 注册的库从本地数据渲染,永远不会请求
api.iconify.design。 - 📦 零配置 —— 开箱自带 Lucide。导入即用,想要更多库再加。
- 🎨 主题自适应 —— 边框、文字、悬停、主色、圆角全部通过
useThemeVars()跟随宿主 Naive UI 主题(明 / 暗 / 自定义主色),无需接任何 CSS 变量。 - 🖼️ 本地 SVG —— 注册你项目里的 SVG,以
local:<名字>的形式选择。 - 🌐 在线兜底 —— 输入任意 Iconify 名称(如
mdi:home),联网时在线加载。 - 🌍 可国际化 —— 所有文案来自一个
labelsprop。默认英文;传入你自己的翻译即可(见 国际化)。 - 🧩 单字符串值 ——
v-model就是一个prefix:name(或local:name)字符串。直接存进数据库字段,任何地方用<OfflineIcon>渲染。
前置要求
这是一个用在既有应用里的组件 —— 它不打包 Vue 和 Naive UI,需要你的应用以 peer 依赖提供:
| Peer 依赖 | 支持范围 |
|---|---|
vue |
^3.3.0 |
naive-ui |
^2.34.0 |
@iconify/vue |
^4.0.0 || ^5.0.0 |
仅限浏览器(用到 navigator.onLine、v-html、Iconify 的 addCollection)。SSR / Nuxt 下请放在仅客户端边界内渲染 —— 见 SSR / Nuxt。
安装
npm i tenon-naive-iconify-picker
# pnpm add tenon-naive-iconify-picker
# yarn add tenon-naive-iconify-picker
样式由组件自动注入,无需单独 import 任何 CSS。
快速开始
零配置:导入即用,内置的 Lucide 库已自动注册。
<script setup lang="ts">
import { ref } from 'vue'
import { IconPicker, OfflineIcon } from 'tenon-naive-iconify-picker'
const icon = ref('lucide:rocket')
</script>
<template>
<!-- 必须在应用的 <n-config-provider> 内,主题才能解析 -->
<IconPicker v-model="icon" />
<!-- 任意位置渲染已存的值 —— 同样离线,也支持 local: -->
<OfflineIcon :icon="icon" :size="18" />
</template>
v-model 保存的是单个字符串,如 lucide:rocket 或 local:star。整个契约就这一点。
图标库(Tab)
你注册的每个图标库都会成为选择器里的一个 Tab,用户可以在不同库之间切换 —— 一个熟悉、较小的库往往比一整个大集更好找。图标数据不打包进本组件:每个库在你的构建里是一个懒加载 chunk,只在第一次点开该 Tab 时加载,用多少付多少(Lucide ≈ 85 KB gz;Phosphor ≈ 946 KB gz)。
# 装你想要的库的数据包
npm i @iconify-json/ant-design @iconify-json/ep @iconify-json/ph
// main.ts
import { setupIconPicker, lucideCollection } from 'tenon-naive-iconify-picker'
setupIconPicker({
collections: [
lucideCollection, // 本包内置
{ prefix: 'ant-design', name: 'Ant Design', loader: () => import('@iconify-json/ant-design/icons.json').then(m => m.default) },
{ prefix: 'ep', name: 'Element Plus', loader: () => import('@iconify-json/ep/icons.json').then(m => m.default) },
{ prefix: 'ph', name: 'Phosphor', loader: () => import('@iconify-json/ph/icons.json').then(m => m.default) },
],
})
保存的值带库前缀(ant-design:home-outlined、lucide:rocket),所以 <OfflineIcon> 总能正确渲染。你也可以用 <IconPicker :collections> prop 按实例传。到 icon-sets.iconify.design 浏览所有可用库;prefix 必须和库匹配。
配置
setupIconPicker(options) —— 可选的一次性配置
在应用启动时调用一次,用来注册图标库、注入本地 SVG、或预热某个库。全部可选;不调用就只有内置的 Lucide。
| 选项 | 类型 | 默认 | 说明 |
|---|---|---|---|
collections |
IconCollection[] |
defaultCollections(Lucide) |
作为 Tab 显示的图标库,每个带懒加载 loader。 |
localIcons |
Record<string, string> |
— | 本地 SVG。接受 { 名字: 原始SVG } 或 import.meta.glob 的结果。 |
preloadPrefix |
string |
第一个库 | 启动时预热哪个库,让首次渲染即时呈现。 |
<IconPicker> props
| Prop | 类型 | 默认 | 说明 |
|---|---|---|---|
v-model |
string |
'' |
保存的值:prefix:name 或 local:name。 |
collections |
IconCollection[] |
全局 / Lucide | 仅覆盖本实例的图标库。 |
localIcons |
Record<string, string> |
— | { 名字: 原始SVG } 或 import.meta.glob 结果。 |
labels |
Partial<IconPickerLabels> |
英文 | 你的翻译 —— 见 国际化。 |
searchIcon / clearIcon |
string |
lucide:search / lucide:x |
搜索/清除图标;请用已注册库里的名字。 |
fallbackIcon |
string |
'' |
空值时 OfflineIcon 的兜底图标。 |
clearable |
boolean |
true |
是否显示清除(×)按钮。 |
cap |
number |
300 |
单页最多渲染多少个图标,超出提示"继续输入以缩小范围"。 |
<OfflineIcon> props
独立的渲染器,用于展示已存的值 —— 表格、菜单、任何地方都行。
| Prop | 类型 | 默认 | 说明 |
|---|---|---|---|
icon |
string |
'' |
prefix:name 或 local:name。 |
size |
number | string |
18 |
数字按 px 处理。 |
fallback |
string |
'' |
icon 为空时渲染它。 |
国际化 (i18n)
组件内部不含任何 i18n 框架,也不假设你用什么。所有可见文案都来自一个 labels 对象 —— 默认英文(defaultLabels)。通过 labels prop 覆盖任意子集;没传的键回退到英文。
文案键
| 键 | 英文默认 |
|---|---|
placeholder |
Select icon |
title |
Select icon |
search |
Search icon name… |
local |
Local SVG |
online |
Online |
onlinePlaceholder |
Any Iconify name, e.g. mdi:home |
use |
Use |
offlineHint |
Offline — online icons that are not bundled cannot be previewed |
loading |
Loading… |
empty |
No matching icon |
more |
{n} more — keep typing to narrow |
more支持{n}占位,渲染时替换为被隐藏的图标数量。
静态覆盖
<IconPicker
v-model="icon"
:labels="{ placeholder: '选择一个图标', title: '图标' }"
/>
接入你自己的 i18n(以 vue-i18n 为例)
把 t() 出来的响应式翻译直接传进去:
<script setup lang="ts">
import { computed } from 'vue'
import { useI18n } from 'vue-i18n'
import { IconPicker } from 'tenon-naive-iconify-picker'
const { t } = useI18n()
// 切换语言时会重新计算。
const iconLabels = computed(() => ({
placeholder: t('iconPicker.placeholder'),
title: t('iconPicker.title'),
search: t('iconPicker.search'),
local: t('iconPicker.local'),
online: t('iconPicker.online'),
onlinePlaceholder: t('iconPicker.onlinePlaceholder'),
use: t('iconPicker.use'),
offlineHint: t('iconPicker.offlineHint'),
loading: t('iconPicker.loading'),
empty: t('iconPicker.empty'),
more: t('iconPicker.more', { n: '{n}' }), // 保留 {n} 占位,交给组件填充
}))
</script>
<template>
<IconPicker v-model="icon" :labels="iconLabels" />
</template>
你的中文语言包大致长这样:
iconPicker: {
placeholder: '选择图标',
title: '选择图标',
search: '搜索图标名称…',
local: '本地 SVG',
online: '在线',
onlinePlaceholder: '任意 Iconify 名称,如 mdi:home',
use: '使用',
offlineHint: '离线 — 未内置的在线图标无法预览',
loading: '加载中…',
empty: '没有匹配的图标',
more: '还有 {n} 个 — 继续输入以缩小范围',
}
本地 SVG 图标
注册你自己的 SVG,让它们出现在 Local SVG Tab 下,并能以 local:<名字> 渲染。
Vite(推荐)—— 用 glob 把 SVG 目录读成原始字符串,文件名(去掉 .svg)就是图标名:
import { registerLocalIcons } from 'tenon-naive-iconify-picker'
registerLocalIcons(
import.meta.glob('/src/assets/svg/*.svg', { query: '?raw', import: 'default', eager: true }),
)
// star.svg -> local:star
(setupIconPicker({ localIcons }) 和 <IconPicker :localIcons> prop 效果相同。)
非 Vite 打包器 —— import.meta.glob 是 Vite 专属。用你工具链暴露原始文件内容的方式构造出 { 名字: 原始SVG } 映射,再传给 registerLocalIcons。组件核心不依赖 Vite。
"离线"是什么意思
注册的库作为懒加载 chunk 从你应用自己的源提供 —— 不会从 Iconify CDN 拉取任何东西。Online Tab 里输入的、你没有注册的那些库的图标,联网时从 Iconify API 加载(离线则不可用)。这是唯一的在线路径,且按图标逐个手动触发。
SSR / Nuxt
选择器用到了仅浏览器的 API。SSR 下请仅在客户端渲染,例如 Nuxt 的 <client-only>,或用 import.meta.client 守卫的动态导入。
开发
npm install
npm run dev # playground(Vite)—— 上面的截图就来自这里
npm run build # dist/(ESM + d.ts,CSS 注入)
npm run typecheck # vue-tsc
许可证
Related Packages
Offline-first icon renderer and picker for Vue 3 + Naive UI, powered by Iconify....
Offline icon assets for vue3-ccui — Iconify mdi collection inlined + tree-shakab...
Use Font Awesome icons with popular Nuxt modules Nuxt Icon and NuxtUI
Humbleicons components for Vue. Designed for ease of use and high performance
HeroIcons components for Vue. Designed for ease of use and high performance
Version History
| Version | Released | Vue | License |
|---|---|---|---|
| 0.1.3 | ^3.3.0 | MIT | |
| 0.1.2 | ^3.3.0 | MIT | |
| 0.1.0 | ^3.3.0 | MIT |