Skip to content

图标

简要介绍

图标是文字的隐喻,可以实现视觉引导和功能划分。

IconFont

Teek 内置 IconFont 阿里巴巴矢量图标库 组件,这是国内功能很强大且图标内容很丰富的矢量图标库,提供矢量图标下载、在线存储、格式转换等功能。

SVG

Teek 内置 SVG 组件,SVG 是使用 XML 来描述二维图形和绘图程序的语言。

Iconify

Teek 内置 Iconify 图标库组件,Iconify 是一个开源、统一的矢量图标库,市面上流行的图标都被收集到该库里,基本满足 99% 的使用场景。

官方链接:

在 Teek 里,Iconify 的图标支持在线模式和本地模式。

图标组件使用

Teek 封装的 icon 组件已经全局注入到 Teek 里,全局注入的位置在 src/main.ts 里。

封装的 Icon 组件为了兼容各个图标类型,使用时需要告诉 Icon 组件使用哪个类型,有 2 种方式:

  1. 传入 iconType 属性:

Icon 组件默认支持如下类型的图标:

ts
export type CommonIconType =
  | "svg"
  | "unicode"
  | "iconfont"
  | "symbol"
  | "img"
  | "component"
  | "iconifyOffline"
  | "iconifyOnline";

使用:

vue
<script setup lang="ts"></script>

<template>
  <Icon icon="icon-dagouyouquan" icon-type="iconfont" />
</template>
  1. 传入特定的前缀:
  • icon 为 img- 或 IMG- 开头,则默认为 img
  • icon 为 if- 或 IF- 开头,则默认为 iconfont
  • icon 为 uni- 或 UNI- 开头,则默认为 unicode
  • icon 为 sym- 或 SYM- 开头,则默认为 symbol
  • icon 为 svg- 或 SVG- 开头,则默认为 svg
vue
<script setup lang="ts"></script>

<template>
  <Icon icon="IF-icon-dagouyouquan" />
</template>

提示

Icon 组件会自动识别传入的 icon 属性是否是一个组件或以图片格式结尾,因此这两类无需传入 iconType 或者添加前缀。

IconFont

Teek 默认已经全局引入了大量常用的 IconFont 图标,如果这些图标仍然无法满足需求,可以去 阿里巴巴矢量图标库 进行收集和下载,下载后建议将其解压到 src/common/assets/iconfont 下。

当然你也可以解压到项目的任意位置,只需要在使用 IconFont 的时候引用下载的 js、css 文件。

如何使用?

假设下载的文件里有一个图标名:icon-dagouyouquan

在组件里这样使用:

vue
<script setup lang="ts">
import "@/assets/iconfont/iconfont.js";
import "@/assets/iconfont/iconfont.css";
</script>

<template>
  <Icon icon="icon-dagouyouquan" icon-type="iconfont" />
  <!-- 或者 -->
  <Icon icon="IF-icon-dagouyouquan" />
</template>

如果你觉得使用 IconFont 时候都需要引用文件:

ts
import "@/assets/iconfont/iconfont.js";
import "@/assets/iconfont/iconfont.css";

那么可以将这两段代码放到 src/main.ts 文件里来进行全局注册,这样就不需要再额外引入这两个文件。

SVG

SVG 图标都以 .svg 结尾,并且 SVG 图标存放的目录必须是一个固定的目录:src/common/assets/icons 下,因为 Teek 需要将所以的本地 SVG 图标进行扫描并注册,因此扫描的目录必须是一个固定的目录,如果你想修改 SVG 存放的目录,则去 node/plugin.ts 文件里修改:

ts
// 使用 svg 图标
createSvgIconsPlugin({
  iconDirs: [resolve(process.cwd(), "src/common/assets/icons")],
  symbolId: "icon-[dir]-[name]",
}),

src/common/assets/icons 就是需要修改 SVG 存放的位置。

如何使用?

SVG 的使用方式非常简单,假设:

  • src/common/assets/icons 下有一个 apply.svg 图标名
  • src/common/assets/icons/core 下有一个 core.svg 图标名
vue
<script setup lang="ts"></script>

<template>
  <!-- 在 icons 目录下找 apply.svg -->
  <Icon icon="apply"></Icon>

  <!-- 在 icons/core 目录下找 bug.svg -->
  <Icon icon="core-bug"></Icon>
</template>

提示

Icon 组件如果不传入前缀或者 iconType,则默认是 SVG 图标

Iconify

在线图标

如果您的项目部署在互联网上,那么可以使用 Iconify 的在线图标,只需要往 Icon 组件传入在线图标名称即可。

如想引用 Element Plus 的 Add Location,就可以这些写:

vue
<script setup lang="ts"></script>

<template>
  <Icon icon="ep:add-location"></Icon>
</template>

其中 ep:add-location 为在线图标名,更多的在线图标请访问:

然后随便点开一个图标库,接着随便点击一个图标,往下看我们就可以看到该图标的在线名字,在线名字一般都是 图标库名 + : + 图标名 作为在线图标名。

如果项目部署在内网,或担心网络访问速度慢导致无法加载图标怎么办?往下看。

离线 JSON 图标

您可以直接将 Iconify 图标以 JSON 方式注册到本地,然后引入到 TkIcon 组件里,如:

sh
pnpm add @iconify-json/ant-design -d

然后在 .vitepress/theme/index.ts 里注册到 Teek 里:

ts
import { addIcons } from "@/components/core/icon";
import icons from "@iconify-json/ant-design/icons.json";

addIcons(icons);

最后和在线方式一样使用 Icon 组件:

html
<Icon icon="ant-design:account-book-filled" />

Icon 优先从已注册的图标名里获取,当获取不到时就会从互联网上下载。

这里演示安装了 ant design 的图标,其他的图标集合根据需要安装。

Iconify JSON 图标的依赖名约定是 @iconify-json/{name},引入 JSON 图标数据的路径约定是 @iconify-json/{name}/icons.json

离线 Icon 图标

您可以直接将 Iconify 的图标集合安装到本地,然后引入到 TkIcon 组件里,如:

sh
pnpm add @iconify-icons/ant-design -d

然后使用:

vue
<script setup>
import Upload from "@iconify-icons/ant-design/upload";
</script>

<Icon :icon="Upload" />

这里演示安装了 ant design 的图标,其他的图标集合根据需要安装。

Iconify Icon 图标的依赖名约定是 @iconify-icons/{name} 或者 @iconify/icons-{name},如:

sh
# pnpm
pnpm add @iconify/icons-ant-design
# 或
pnpm add @iconify-icons/ant-design

两个离线图标方式对比

  • JSON 图标方式需要在项目初始化时注册进去,后续直接通过字符串引用
  • Icon 图标方式在每次使用时需要手动引入

信息

Icon 并没有实现 Iconify 相关逻辑,而是通过代理 Iconify 的 API 实现。

组件图标

Teek 也支持传入组件图标,如 ElementPlus 的图标:

vue
<script setup>
import { Odometer } from "@element-plus/icons-vue";
</script>

<template>
  <Icon :icon="Odometer" />
  <!-- 或者 -->
  <Icon>
    <Odometer />
  </Icon>
</template>

组件图标不需要传入前缀或者 iconType,内部会自动识别。

图片

如果传入的 icon 带有 .png.jpg.jpeg.gif.bmpwebp 后缀,则 Teek 会将图标渲染为图片(使用 img 标签)。

html
<Icon icon="https://xxx/logo.png" />

全局注册图标

如果 Teek 要使用高频的图标,我们需要经常 import from 会显得很麻烦,那么我们可以在 Teek 加载的时候,往 Teek 注入一些高频使用的图标,然后引用的时候,直接填写图标名,不需要引入。

Element Plus

如果是 ElementPlus 图标,需要在 src/main.ts 里使用 app 来全局注册。

全部引入:

ts
import * as ElementPlusIconsVue from "@element-plus/icons-vue";

for (const [key, component] of Object.entries(ElementPlusIconsVue)) {
  app.component(key, component);
}

key 是图标的名字。

按需引入:

ts
import { Edit } from "@element-plus/icons-vue";

app.component("edit", Edit);

使用:

vue
<Icon icon="edit"></Icon>

Iconify

如果是 Iconify 图标,可以使用 addIcon 函数来加载图标,该函数将将图标全局注册到 Teek 里。

src/layout/index.vue 里添加如下:

ts
import { addIcon } from "@iconify/vue";
import Edit from "@iconify-icons/ep/edit";

addIcon("edit", Edit);

addIcon 的第一个参数就是该图标的名字,使用的时候引用该名字就可以了:

vue
<Icon icon="edit"></Icon>

如果 addIcon 比较多,可以单独放到一个文件里,如将代码放到 src/layout/offlineIcon.ts 下,然后在 src/layout/index.vue 引入:

ts
import "@/layout/offlineIcon";

Icon 组件 Props 类型

Icon 组件可以传入的 props 类型:

ts
import type { Component } from "vue";
import type { IconifyIcon } from "@iconify/vue";

export type CommonIconType =
  | "svg"
  | "unicode"
  | "iconfont"
  | "symbol"
  | "img"
  | "component"
  | "iconifyOffline"
  | "iconifyOnline";

export type CommonIcon = string | Object | Component | IconifyIcon;

export interface IconProps {
  /**
   * 图标
   */
  icon?: CommonIcon;
  /**
   * 图标类型
   */
  iconType?: CommonIconType;
  /**
   * 图标大小
   *
   * @default 'inherit'
   */
  size?: string | number;
  /**
   * 图标颜色
   *
   * @default 'inherit'
   */
  color?: string;
  /**
   * 图标是否可悬停
   *
   * @default false
   */
  hover?: boolean;
  /**
   * 图标悬停时的颜色,仅当 hover 为 true 时有效
   */
  hoverColor?: string;
  /**
   * img 标签的 alt,当 iconType 为 img 时生效
   */
  imgAlt?: string;
  /**
   * 是否使用鼠标手形
   */
  pointer?: boolean;
  /**
   * 自定义图标样式
   */
  style?: Record<string, any>;
}

使用例子:

vue
<Icon icon="IF-icon-dagouyouquan" :size="200" />

Icon 组件原理

Teek 封装了 Icon 组件来满足不同的使用场景,那么原理是什么呢?

封装 Icon 的相关文件都在 src/components/core/icon 下。

我们可以点进去看 Icon 组件源码,可以看到 Icon 组件的引用:

ts
import SvgIcon from "./components/svg-icon.vue";
import FontIcon from "./components/font-icon.vue";
import IconifyOnline from "./components/iconify-online.vue";
import IconifyOffline from "./components/iconify-offline.vue";

于是我们可以知道,Icon 组件就类似于一个集成入口,它内置了:

  • SVG 组件:SvgIcon
  • IconFont 组件:FontIcon
  • 在线 Iconify 组件:IconifyOnline
  • 本地 Iconify 组件:IconifyOffline

Icon 通过传入的 IconType 属性或者 icon 前缀规则来判断属于哪一类图标,从而渲染对应的组件。

当然你也可以直接对应的图标组件:

vue
<script setup lang="ts">
import { FontIcon } from "@/components/core/icon";

import "@/assets/iconfont/iconfont.js";
import "@/assets/iconfont/iconfont.css";
</script>

<template>
  <FontIcon icon="icon-dagouyouquan"></FontIcon>
</template>
vue
<script setup lang="ts">
import { SvgIcon } from "@/components/core/icon";
</script>

<template>
  <SvgIcon icon="bug"></SvgIcon>
</template>
vue
<script setup lang="ts">
import { IconifyOnline } from "@/components/core/icon";
</script>

<template>
  <IconifyOnline icon="ep:add-location"></IconifyOnline>
</template>
vue
<script setup lang="ts">
import { IconifyOffline } from "@/components/core/icon";
import Upload from "@iconify-icons/ant-design/upload";
</script>

<template>
  <IconifyOffline icon="Upload"></IconifyOffline>
</template>

VSCode 图标插件

使用 Iconify 并且开发 IDE 是 VSCode,那么可以安装 antfu.iconify 插件,该插件可以在引用 Iconify 的图标时,直接在代码里显示该图标。