导航栏图标
导航栏添加图标
HTML 方式
在 .vitepress/config.mts
的 themeConfig.nav
配置项中添加图标 HTML:
ts
import { defineConfig } from "vitepress";
export default defineConfig({
themeConfig: {
nav: [
{
text: `
<div style="display: flex; align-items: center; gap: 4px;">
<img src="your img link" alt="" style="width: 16px; height: 16px;">
<span>指南</span>
</div>
`,
link: "http://vp.teek.top/",
},
],
},
});
组件方式
警告
组件方式不能作用在父级导航上。
上面的方法需要传入一个 HTML 标签来实现,如果有很多导航配置,那么写起来很麻烦,且可读性较差,
在 .vitepress/theme/components
自定义一个组件 NavIcon.vue
来进行封装:
vue
<!-- .vitepress/theme/components/NavIcon.vue -->
<script setup lang="ts">
import type { DefaultTheme } from "vitepress/theme";
import type { TkIconProps } from "vitepress-theme-teek";
import { useData } from "vitepress";
import { TkIcon, isClient } from "vitepress-theme-teek";
import { VPLink } from "vitepress/theme";
defineProps<DefaultTheme.NavItemWithLink & { iconProps?: TkIconProps; subMenu?: boolean }>();
const { page } = useData();
const HASH_RE = /#.*$/;
const HASH_OR_QUERY_RE = /[?#].*$/;
const INDEX_OR_EXT_RE = /(?:(^|\/)index)?\.(?:md|html)$/;
const isActive = (currentPath: string, matchPath?: string, asRegex: boolean = false) => {
if (matchPath === undefined) return false;
currentPath = normalize(`/${currentPath}`);
if (asRegex) return new RegExp(matchPath).test(currentPath);
if (normalize(matchPath) !== currentPath) return false;
const hashMatch = matchPath.match(HASH_RE);
if (hashMatch) return (isClient ? location.hash : "") === hashMatch[0];
return true;
};
const normalize = (path: string) => {
return decodeURI(path).replace(HASH_OR_QUERY_RE, "").replace(INDEX_OR_EXT_RE, "$1");
};
</script>
<template>
<div :class="{ Icon: iconProps?.icon }">
<VPLink
:class="{
VPNavBarMenuLink: !subMenu,
SubMenu: subMenu,
active: isActive(page.relativePath, activeMatch || link, !!activeMatch),
}"
:href="link"
:target="target"
:rel="rel"
:no-icon="noIcon"
tabindex="0"
>
<TkIcon v-if="iconProps?.icon" v-bind="iconProps" />
<span v-html="text"></span>
</VPLink>
</div>
</template>
<style scoped>
.VPNavBarMenuLink {
display: flex;
align-items: center;
padding: 0 12px;
line-height: var(--vp-nav-height);
font-size: 14px;
font-weight: 500;
color: var(--vp-c-text-1);
transition: color 0.25s;
}
.VPNavBarMenuLink.active {
color: var(--vp-c-brand-1);
}
.VPNavBarMenuLink:hover {
color: var(--vp-c-brand-1);
}
.tk-icon {
margin-right: 7px;
}
.SubMenu {
display: flex;
align-items: center;
border-radius: 6px;
padding: 0 12px;
line-height: 32px;
font-size: 14px;
font-weight: 500;
color: var(--vp-c-text-1);
white-space: nowrap;
transition:
background-color 0.25s,
color 0.25s;
}
.SubMenu:hover {
color: var(--vp-c-brand-1);
background-color: var(--vp-c-default-soft);
}
</style>
然后在 .vitepress/theme/index.ts
里全局注册
ts
// .vitepress/theme/index.ts
import Teek from "vitepress-theme-teek";
import "vitepress-theme-teek/index.css";
import NavIcon from "./components/NavIcon.vue";
export default {
extends: Teek,
Layout: Teek.Layout,
enhanceApp({ app }) {
app.component("NavIcon", NavIcon);
},
};
最后在 themeConfig.nav
使用:
ts
import { defineConfig } from "vitepress";
export default defineConfig({
themeConfig: {
nav: [
{
component: "NavIcon",
props: {
text: "指南",
link: "/guide/intro",
activeMatch: "/01.指南/",
iconProps: {
icon: "https://vp.teek.top/teek-logo-mini.svg",
iconType: "img",
},
},
},
{
text: "资源",
items: [
{
component: "NavIcon",
props: {
text: "案例",
link: "/case",
subMenu: true,
iconProps: {
icon: "https://vp.teek.top/teek-logo-mini.svg",
iconType: "img",
size: 12, // 大小
},
},
},
{
component: "NavIcon",
props: {
text: "常见问题",
link: "/theme/qa",
subMenu: true,
iconProps: {
icon: "https://vp.teek.top/teek-logo-mini.svg",
iconType: "img",
},
},
},
],
},
],
},
});
NavIcon
组件的props
里的配置项和VitePress
的nav
配置项一致iconProps
是TkIcon
组件的props
,更多具体 API 用法请参考 TkIcon 组件