- 前端
【免费下载链接】BewlyBewly
Just make a few small changes to your Bilibili homepage. (English | 简体中文 | 正體中文 | 廣東話)
导读
本篇技术指南聚焦 BewlyBewly 仓库中 src/components/README.md 所定义的组件图标使用规范:项目借助 Iconify 的开放图标生态,在 Vue 组件中按需引用任意图标集的图标,并通过打包器插件保证"只用到的图标才进入产物"。读完本文,你将掌握@iconify/vue运行时组件、i-前缀的 UnoCSS 原子类、以及@iconify/json+presetIcons的按需编译原理,并能在自己开发的 BewlyBewly 组件或类似 Vue 项目中直接复用这套图标方案。
一、原文档核心要点:一句话的图标使用约定
src/components/README.md全文极其精简,但信息密度集中,其核心结论可归纳为三条:
- 几乎所有图标集都可用:通过 Iconify 的力量,
mdi、mingcute、tabler、line-md、solar、uil等数十个开源图标集均可直接使用,无需为每个图标集单独安装前端运行时依赖。 - 按需打包:项目"只会打包你用到的图标"(It will only bundle the icons you use),避免把整个图标集(例如
@iconify/json中动辄数万枚图标)全部塞进扩展产物。 - 工具链支撑:按需能力来自
vite-plugin-icons(antfu 系插件)这类打包器插件,在本仓库中对应为 UnoCSS 的presetIcons预设。
这三条约定贯穿了整个src/components/目录的图标使用实践,下面结合仓库源码逐条展开验证与实操。
二、运行时组件方案:@iconify/vue的<Icon>组件
2.1 依赖与安装
在 package.json 的devDependencies中可以确认,BewlyBewly 使用了两套互补的图标依赖:
@iconify/vue(^4.1.2):Iconify 官方提供的 Vue 3 运行时组件,通过<Icon icon="集合:名称">按需渲染 SVG;@iconify/json(^2.2.219):包含了大量图标集的 JSON 数据源,配合 UnoCSS 的presetIcons在构建期把用到的图标编译为 CSS/内联数据。
仓库源码不直接依赖某个具体图标集的前端包(如@mdi/js、@material-design-icons/svg),这正是 Iconify 模式的典型特征:数据与渲染解耦。
2.2 使用示例:回到顶部/刷新按钮
src/components/BackToTopOrRefreshButton.vue 是最直观的用法示例:
<script setup lang="ts"> import { Icon } from '@iconify/vue' import Button from '~/components/Button.vue' import { useBewlyApp } from '~/composables/useAppProvider' const emit = defineEmits(['refresh', 'backToTop']) const { reachTop } = useBewlyApp() </script> <template> <Button ... @click="reachTop ? emit('refresh') : emit('backToTop')"> <Transition name="fade"> <Icon v-if="reachTop" icon="line-md:rotate-270" shrink-0 rotate-90 absolute text-2xl /> <Icon v-else icon="line-md:arrow-small-up" shrink-0 absolute text-2xl /> </Transition> </Button> </template>要点拆解:
import { Icon } from '@iconify/vue'之后,模板中直接书写<Icon icon="...">;icon属性的值采用集合前缀:图标名称格式,例如line-md:arrow-small-up表示line-md(Line MD 动态线条风格)图标集中的arrow-small-up图标;- 结合
v-if/v-else与 Vue 的<Transition>,可以实现"回到顶部图标 ⇄ 刷新图标"的平滑淡入淡出切换; @iconify/vue渲染的是内联 SVG,天然支持通过text-2xl这类 UnoCSS 工具类控制尺寸。
2.3 更复杂的运行时案例:Dock 中的主题切换动效
src/components/Dock/Dock.vue 展示了Icon组件与暗色模式状态机结合的高级用法:
<Transition name="fade"> <div v-show="hoveringDockItem.themeMode" absolute> <Icon v-if="isDark" icon="line-md:sunny-outline-to-moon-loop-transition" /> <Icon v-else icon="line-md:moon-alt-to-sunny-outline-loop-transition" /> </div> </Transition> <Transition name="fade"> <div v-show="!hoveringDockItem.themeMode" absolute> <Icon v-if="isDark" icon="line-md:sunny-outline-to-moon-transition" /> <Icon v-else icon="line-md:moon-to-sunny-outline-transition" /> </div> </Transition>这里用到了line-md图标集独有的过渡动画图标(-loop-transition/-transition后缀),当鼠标悬浮时图标会从"太阳"平滑过渡到"月亮"或反向循环。这也印证了 README 中"可以使用几乎所有图标集"的能力:不仅静态图标,连带动效的图标同样开箱即用。
在 src/components/TopBar/components/MomentsPop.vue 与 src/components/VideoCard/VideoCard.vue 中,<Icon icon="line-md:confirm" />等写法同样随处可见,说明@iconify/vue是src/components/下最主流的运行时图标方案。
三、构建期原子类方案:UnoCSSpresetIcons与i-前缀
3.1 配置源头
虽然 README 提到的是vite-plugin-icons,但 BewlyBewly 实际使用的按需方案是UnoCSS 的presetIcons预设,配置位于 unocss.config.ts:
import { presetAttributify, presetIcons, presetTypography, presetUno, transformerDirectives } from 'unocss' import { defineConfig } from 'unocss/vite' export default defineConfig({ presets: [ presetUno(), presetAttributify(), presetIcons({ extraProperties: { 'display': 'inline-block', 'vertical-align': 'middle', 'width': '1.2em', 'height': '1.2em', }, }), presetTypography(), // ... ], })presetIcons的extraProperties为每个由原子类生成的图标统一注入了内联块布局与1.2em默认尺寸,这使得所有i-图标天然对齐文本基线、随font-size缩放。同时,content.pipeline.include配置('**/*.{js,ts}'与\.(vue|svelte|[jt]sx|mdx?|astro|elm|php|phtml|html)($|\?))决定了扫描哪些文件中的i-类名,构建时只提取真实出现的图标。
3.2 原子类写法与实战示例
在组件模板中直接书写i-集合:图标名即可生成图标,例如 src/components/SearchBar/SearchBar.vue:
<div i-tabler:search block align-middle />src/components/Settings/About/About.vue 集中展示了多图标集混用:
<div i-tabler:brand-github /> GitHub <div i-tabler:brand-bilibili /> Bilibili <div i-tabler:brand-discord /> Discord <div i-tabler:brand-twitter /> Twitter <div i-tabler:heart /> {{ $t('settings.sponsor') }}这里一口气用到了tabler、uil、mingcute、solar等多个图标集,再次证明 README 所述"从几乎所有图标集取用"的能力。UnoCSS 在构建期会将它们转换为内联 SVG 背景或 data URI,未使用的图标不会进入产物。
3.3 动态图标类名:把图标名放进配置数据
UnoCSS 的按需扫描对静态字符串最可靠,但 BewlyBewly 中多处出现"图标名存于数据、运行时拼类名"的场景,例如 src/components/Settings/BewlyPages/BewlyPages.vue:
{ icon: 'i-mingcute:home-5-line', iconActivated: 'i-mingcute:home-5-fill', }, { icon: 'i-mingcute:search-2-line', iconActivated: 'i-mingcute:search-2-fill', },模板中通过:class="activePage === page.value ? page.iconActivated : page.icon"动态切换普通态与激活态两枚图标。这要求扫描器能索引到这些字符串——好消息是这些i-名称以完整字面量出现在<script setup>里,UnoCSS 默认即可捕获(unocss.config.ts 的 include 规则覆盖了.ts文件)。
同样,src/components/Dock/Dock.vue 从mainStore.dockItems读取icon/iconActivated后,通过:class="dockItem.icon"渲染,配合text-xl统一尺寸;设置面板的 src/components/Settings/Settings.vue 也采用i-mingcute:settings-3-line/i-mingcute:imac-line等写法管理页面图标。若图标名在纯运行时拼接产生(如模板字符串拼类名),则无法被静态扫描,这是使用 UnoCSS 图标方案时需要避开的坑。
四、两种方案的协同分工与选择建议
综合源码使用情况,可以总结出 BewlyBewly 的图标实践存在两条清晰的路径:
| 维度 | @iconify/vue运行时组件 | UnoCSSpresetIcons+i-类 |
|---|---|---|
| 典型写法 | <Icon icon="line-md:rotate-270" /> | <div i-tabler:search /> |
| 依赖 | @iconify/vue(package.json) | @iconify/json+unocss(package.json) |
| 渲染时机 | 运行时渲染内联 SVG | 构建期编译为 CSS/内联数据 |
| 产物体积 | 仅打包用到的图标数据 | 仅生成用到的图标规则 |
| 适用场景 | 需要动态绑定icon名称、条件切换 | 静态写死在模板中的图标、跟随字号缩放 |
| 仓库示例 | BackToTopOrRefreshButton.vue、Dock.vue | SearchBar.vue、About.vue |
选择建议:
- 图标名来自用户配置、接口数据或需要
v-if动态切换时,优先用@iconify/vue的<Icon>; - 图标固定出现在模板中、希望尺寸随字号弹性缩放时,优先用
i-集合:图标名原子类(extraProperties已默认1.2em宽高); - 两种方式都遵循"按需打包",可放心混用,仓库中
src/components/的多个组件正是二者并用。
五、如何在 BewlyBewly 中新增一个使用图标的组件
结合以上机制,在仓库中新增组件时可按如下步骤接入图标:
- 确认图标可用:在 Iconify 图标浏览器中找到目标图标,记录其
集合:名称(如mingcute:close-line),并确认@iconify/json已覆盖该集合(默认全量安装,package.json)。 - 静态图标用原子类:在模板中直接写
<div i-mingcute:close-line />,参照 IframeDrawer.vue 中i-mingcute:external-link-line与i-mingcute:close-line的用法。 - 动态图标用组件:
import { Icon } from '@iconify/vue',再用:icon="someVar"绑定,参照 VideoCard.vue 的<Icon icon="line-md:confirm" />。 - 统一视觉:静态图标建议保留默认
1.2em尺寸;需要更大尺寸时叠加text-xl、text-2xl等字号类(如 Dock.vue 中的text-xl),图标会随字号等比缩放。 - 本地验证:运行
pnpm install后执行pnpm dev(见 package.json),打开 Bilibili 首页查看新组件图标是否正确渲染;最终产物通过pnpm build(package.json)生成,可用pnpm lint(package.json)与pnpm typecheck(package.json)做质量把关。
六、按需打包原理小结
回到 README 的核心承诺 "It will only bundle the icons you use",其落地机制可以总结为:
- 运行时路径:
@iconify/vue从@iconify/json中按icon属性的取值按需导入对应图标数据,未引用的图标不会被打包进扩展; - 构建期路径:UnoCSS
presetIcons扫描content.pipeline.include匹配的源码文件(unocss.config.ts),仅对出现的i-集合:图标名生成样式规则; - 数据来源统一:两类方案共享
@iconify/json这一图标数据底座(package.json),因此 README 才能宣称"几乎所有图标集"都可以直接用,而仓库无需为每个图标集引入额外运行时依赖。
这套"Iconify 数据 + 运行时组件/构建期编译双通道"的设计,让 BewlyBewly 在保持 UI 图标风格统一的同时,将扩展产物体积控制在"只用多少、打包多少"的范围内,值得在同类浏览器扩展与 Vue 应用中直接借鉴。
- 前端
【免费下载链接】BewlyBewly
Just make a few small changes to your Bilibili homepage. (English | 简体中文 | 正體中文 | 廣東話)
相关推荐
Iconify API终极指南:如何实现200,000个图标的按需加载
Iconify API终极指南:如何实现200,000个图标的按需加载 Iconify是当今最强大的统一图标框架,它通过智能的API按需加载机制,让开发者能够轻
前端UI组件PrimeVue 动态导入(Dynamic Imports)实战指南:按需加载组件与图标
PrimeVue 动态导入(Dynamic Imports)实战指南:按需加载组件与图标 动态导入(Dynamic Imports)让 PrimeVue 开发者
前端UI组件设计系统G-Helper AMD 降压教程:Ryzen 笔记本降温省电,性能几乎不损失
G Helper AMD 降压教程:Ryzen 笔记本降温省电,性能几乎不损失 G Helper 的 AMD CPU 降压功能,可以把 Ryzen 处理器的供电
桌面应用系统编程
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考