图标使用
Teek 默认注册了全局组件 TkIcon,因此你可以通过该组件快捷引入图标。
TkIcon 默认支持如下类型的图标:
- svg
- unicode
- iconfont
- symbol
- img
- component
- iconifyOffline
- iconifyOnline
除此之外,您可以通过默认插槽传入自定义图标组件。
TkIcon 组件的基础使用以及 API 介绍请看 Icon 图标。
提示
Teek 所有的 Icon 图标相关配置项,都使用 TkIcon 组件,因此怎么使用 TkIcon 的 icon 属性,就怎么使用图标相关配置项。
SVG 图标
您可以下载一个 svg 图标到项目里,然后传入 TkIcon 组件中。
在 Markdown 文档有两种格式输入:
<script setup>
const icon = `<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 1024 1024"><path fill="currentColor" d="M512 320 192 704h639.936z"></path></svg>`;
</script>
<TkIcon :icon="icon" /><TkIcon>
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 1024 1024">
<path fill="currentColor" d="M512 320 192 704h639.936z"></path>
</svg>
</TkIcon>输出:
SVG 图标在哪里获取?您可以访问 阿里巴巴矢量图标库,然后搜索需要的图标,最后在下载时选择 复制 SVG 图标。
除此之外,您也可以可以在该网站上下载 Unicode、Font Class、Symbol 图标,然后传入 TkIcon 组件中。
如:
<TkIcon :icon="icon-info" />
<!-- 如果是 Font Class,则等于如下 -->
<i class="iconfont icon-info"></i>Iconify 图标
在线图标
如果您的项目部署在互联网上,那么可以使用 Iconify 的在线图标,只需要往 TkIcon 组件传入在线图标名称即可。
在 Markdown 文档输入:
<TkIcon icon="mdi:github" />输出:
其中 mdi:github 为在线图标名,更多的在线图标请访问:Iconify 在线图标。
如果项目部署在内网,或担心网络访问速度慢导致无法加载图标怎么办?往下看。
离线 JSON 图标
您可以直接将 Iconify 图标以 JSON 方式注册到本地,然后引入到 TkIcon 组件里,如:
pnpm add @iconify-json/ant-design -d然后在 .vitepress/theme/index.ts 里注册到 Teek 里:
import { addIcons } from "vitepress-theme-teek";
import icons from "@iconify-json/ant-design/icons.json";
addIcons(icons);最后和在线方式一样使用 TkIcon 组件:
<TkIcon icon="ant-design:account-book-filled" />TkIcon 优先从已注册的图标名里获取,当获取不到时就会从互联网上下载。
这里演示安装了 ant design 的图标,其他的图标集合根据需要安装。
Iconify JSON 图标的依赖名约定是 @iconify-json/{name},引入 JSON 图标数据的路径约定是 @iconify-json/{name}/icons.json。
离线 Icon 图标
您可以直接将 Iconify 的图标集合安装到本地,然后引入到 TkIcon 组件里,如:
pnpm add @iconify-icons/ant-design -d然后使用:
<script setup>
import Upload from "@iconify-icons/ant-design/upload";
</script>
<TkIcon :icon="Upload" />这里演示安装了 ant design 的图标,其他的图标集合根据需要安装。
Iconify Icon 图标的依赖名约定是 @iconify-icons/{name} 或者 @iconify/icons-{name}。
两个离线图标方式对比
- JSON 图标方式需要在项目初始化时注册进去,后续直接通过字符串引用
- Icon 图标方式在每次使用时需要手动引入
信息
TkIcon 并没有实现 Iconify 相关逻辑,而是通过代理 Iconify 的 API 实现。
内置图标
SVG 图标
下面展示 Teek 内置的主题图标集合,Teek 只保留了自身引用的图标,您可以在页面上随处可见,如果您需要额外的图标,请参考上面的方式添加。
<ul class="demo-icons">
<li v-for="(icon, name) in TkIcons" class="flx-column-center" @click="handleCopySvg(name)">
<TkIcon :icon="icon" :size="30" />
<span>{{ name }}</span>
</li>
</ul>当点击图标后将会复制引用图标代码(参考下面高亮部分)到您的剪切板中,然后就可以粘贴到代码中:
<script setup>
import { yourClickIconName } from "vitepress-theme-teek/icons";
</script>
<TkIcon :icon="yourClickIconName" />社交图标(iconfont)v1.4.1
如下是 Teek 内置的社交图标集合,可以在导航栏、侧边栏、社交配置 social 里快速应用。
<ul class="demo-icons">
<li v-for="(icon) in iconfont" class="flx-column-center" @click="handleCopyIconfont(icon)">
<i :class="`iconfont ${icon}`" style="font-size: 24px"></i>
<span>{{ icon }}</span>
</li>
</ul>如果 Teek 提供的社交图标不满足您的要求,您可以使用访问 阿里巴巴矢量图标库 来下载您需要的任何图标。
<script lang="ts" setup>
import { TkMessage, useClipboard, isClient } from "vitepress-theme-teek";
import * as TkIcons from "vitepress-theme-teek/icons";
const { copy, copied } = useClipboard();
const icon = `<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 1024 1024"><path fill="currentColor" d="M512 320 192 704h639.936z"></path></svg>`;
const handleCopySvg = async (name: string) => {
await copy(`import { ${name} } from "vitepress-theme-teek/icons";`);
copied.value
? TkMessage.success({ message: "复制成功!", plain: true })
: TkMessage.error({ message: "复制失败!", plain: true });
};
const { extractIconClasses } = useIconfont();
const iconfont = extractIconClasses().map(item => item.className);
const handleCopyIconfont = async (icon: string) => {
await copy(`<i class="iconfont ${icon}"></i>`);
copied.value
? TkMessage.success({ message: "复制成功!", plain: true })
: TkMessage.error({ message: "复制失败!", plain: true });
};
/**
* iconfont 图标提取工具
*/
function useIconfont() {
interface IconfontType {
className: string;
unicode?: string;
}
/**
* 提取单个 CSS 规则中的 iconfont 信息
*/
function extractIconFromRule(rule: CSSRule): IconfontType | null {
if (!(rule instanceof CSSStyleRule)) return null;
const { selectorText, style } = rule;
// 只处理以 .icon- 开头且包含 ::before 的选择器
if (
!selectorText?.startsWith(".icon-") ||
!selectorText.includes("::before")
)
return null;
const className = selectorText.substring(
1,
selectorText.indexOf("::before")
);
const content = style.getPropertyValue("content");
if (!content) return null;
const unicode = content.replace(/['"\\]/g, "");
return {
className,
unicode: unicode ? `&#x${getUnicode(unicode)};` : undefined,
};
}
/**
* 提取所有样式表中的 iconfont class 信息
*/
function extractIconClasses(): IconfontType[] {
if(!isClient) return [];
const iconInfos: IconfontType[] = [];
const processedSheets = new WeakSet<CSSStyleSheet>();
for (const sheet of Array.from(document.styleSheets)) {
// 防止跨域或重复处理
if (processedSheets.has(sheet)) continue;
processedSheets.add(sheet);
let rules: CSSRuleList | undefined;
try {
rules = sheet.cssRules;
} catch {}
if (!rules) continue;
for (const rule of Array.from(rules)) {
const iconInfo = extractIconFromRule(rule);
if (iconInfo) {
iconInfos.push(iconInfo);
}
}
}
return iconInfos;
}
/**
* 获取字符的 unicode 16 进制字符串
* @param charCode 字符
*/
const getUnicode = (charCode: string): string => {
if (!charCode) return "";
return charCode.codePointAt(0)?.toString(16).padStart(4, "0") ?? "";
};
return { extractIconClasses };
}
</script>
<style>
.demo-icons {
display: grid;
grid-template-columns: repeat(auto-fill, minmax(120px, 1fr));
overflow: hidden;
list-style: none !important;
border-left: 1px solid var(--vp-c-divider);
border-top: 1px solid var(--vp-c-divider);
border-radius: 4px;
padding: 0 !important;
}
.demo-icons li {
text-align: center;
height: 90px;
border-right: 1px solid var(--vp-c-divider);
border-bottom: 1px solid var(--vp-c-divider);
transition: background-color 0.3s;
cursor: pointer;
margin: 0 !important;
}
.demo-icons span {
margin-top: 8px;
font-size: 13px;
}
.demo-icons li:hover {
background-color: var(--tk-fill-color-light);
}
</style>