UnoCSS动态图标icon无法显示解决方案
发布于:(更新于:)
这几天遇到了一个奇怪的问题,使用UnoCSS过程中,有的icon图标能够正常显示,但是有的不能,当然最糟糕的事,我找了几天,才发现诡异之处。
问题描述
首先UnoCSS是无法通过动态的方式解析到你可能用到的icon,主要是以下2个场景可能会无法显示icon图标:
- 后台管理系统的菜单,由后端返回菜单数据信息包含icon类名或在路由文件的meta信息中包含,由模板遍历数据来显示(我就是在这个场景下出现问题的)
- 自己封装icon组件、通过props传递icon图标信息进行拼接的
很显然,稍微了解UnoCSS引擎的朋友,大概可以推测到,动态加载进去的图标类名无法被检测到,所以也就不能显示了,如何解决这个问题呢。
解决方案
【2023年08月15日】更新
在完成 UnoCSS v0.55.0
的一个入门教程UnoCSS-Study-Examples的时候,我仔细阅读官方文档,发现有一个新的配置项可以完美解决这个问题。这里称之为终极解决方案。
而文中的需求你可能还是要看下,这里直接说方案。
完美方案:调整UnoCSS配置文件
将需要被扫描的文件添加到配置中,比如我项目中的路由文件路径 src/router/index.ts
,那么在 uno.config.ts
中添加一段:
export default defineConfig({ // ... content: { pipeline: { include: [ // the default /\.(vue|svelte|[jt]sx|mdx?|astro|elm|php|phtml|html)($|\?)/, // 这里只写我需要的,当然你也可以定制,参考:https://unocss.dev/guide/extracting#extracting-from-build-tools-pipeline "src/router/index.ts", ], // exclude files // exclude: [] } } )}
这样就免去了之前写 // @unocss-include
的麻烦,也更好管理。
与此同时,官方还出了异步方式来提前解析可能存在的各种特殊情况,这里我就不赘述了,可以看完整的说明:动态加载icon。那么这个动态加载无论是icon还是一些特殊的class名,都能较好的处理了~
因为我是在做菜单组件时候发现的问题,所以以菜单的场景举例说明。
方案一:图标预加载
在菜单组件写一段“没有用”的代码
<div v-if="false" i-mdi-poll i-mdi-monitor-dashboard i-mdi-ballot-recount-outline i-mdi-application-brackets-outline
i-mdi-apache-kafka i-mdi-archive-search-outline i-mdi-format-text-wrapping-overflow></div>
当然你不想用 v-if
换成 hidden
也是可以的。同样的你想统一管理这所有的动态加载的icon,也可以专门写到 index.html
进行补充,因为默认状态下这些都能被UnoCSS识别到。
方案二:项目文件添加到UnoCSS扫描范围【推荐】
其实这个方案就很不错了,比如我是Vue的菜单组件中调用路由( router/menu.ts
)内的每条路由记录的meta中icon,那么就给这个 menu.ts
文件顶部加一段注释
// @unocss-include
export const menuRoutes = [{
name: "Index",
path: "/",
component: () => import("@/layout/index.vue"),
isHidden: true,
redirect: "/dashboard/overview",
meta: {
title: "首页",
},
children: [{
name: "Dashboard",
path: "/dashboard",
component: () => import("@/views/dashboard/index.vue"),
redirect: "/dashboard/overview",
meta: {
title: "看板",
icon: "i-mdi-poll",
},
// ...
}]
}]
这样就解决了。相同的原理,如果你想统一管理,也可以创建一个单独的文件在src目录下,比如叫做 unocss-icon.ts
,然后再在 main.ts
内引入就好了。这完全取决于你的喜好~
// unocss-icon.ts // @unocss-include const iconList = ["i-mdi-poll", "i-mdi-monitor-dashboard", "i-mdi-ballot-recount-outline", "i-mdi-application-brackets-outline", "i-mdi-apache-kafka", "i-mdi-archive-search-outline", "i-mdi-format-text-wrapping-overflow" ];
// main.js // 添加一行 import "./unocss-icon";
后记
我在Github上也做了一个简单的模板,大家可以参考一下:Vite-Element-Plus-UnoCSS
另外,为啥看起来挺简单的问题,我搞了几天。原因有几点:
- 才上手
UnoCSS
不到一周,虽然主要功能了解,但细节仍有许多不清楚。 - 我做菜单时,最早写的纯静态Vue组件,用的
el-menu
,没有结合路由,并且只给一级菜单加了图标,后来改造的时候,把旧的代码注释掉了,但没删除,然后通过menu.ts
动态获取,这时,因为旧的Vue组件没有删,所以旧的图标还在,但是我给二级菜单添加的图标是之前没有的,所以造成了有的图标有,有的没有,就是旧代码注释未删除造成的假象。这也算把自己坑了~
而上面提到的解决方案之前是没有搜出来的,也没想到是因为不支持动态加载图标造成的,当我后来反复摸索发现了这个问题,才去 issue
寻找是否有人遇到类似的情况,发现确实有。
antfu
也做了一些解释,相关的issues我整理如下:
- 从ts中引入icon名称加载为动态calss时,图标不显示
- How to dynamically import icons instead of directly use them in templates
- v-bind:class with template string utilities not detected
最后,最佳解决方案也是参考:UnoCSS - Scanning
whidy
一名爱折腾的前端开发工程师,喜欢打篮球和分享 ฅʕ•̫͡•ʔฅ