更多功能
你可能发现 Fork 的 izhichao/vitepress-theme-minimalism 启动后和我的博客相比还少了一些功能,这是因为我的博客还修改了部分 CSS 以及安装了一些第三方的插件,但这些内容不太适合直接集成到主题中。
如果你也需要这些功能可以按照以下步骤自行添加。
图注
默认情况下,VitePress 的图片下方是没有图注的,可以借助 @mdit/plugin-figure 这个插件自动为图片添加描述。
例如, 其中的 标题 就会自动添加到图片的下方。
安装依赖
pnpm add @mdit/plugin-figure -D导入
import { defineConfig } from 'vitepress'
import { figure } from '@mdit/plugin-figure';
export default defineConfig({
markdown: {
config(md) {
md.use(figure);
},
},
})代码块图标
先前一直以为代码块图标是 VitePress 默认主题自带的,因为官方文档里就有图标,但是自己无论怎么配置就是没有图标,后面才知道原来官方文档也是用了 vitepress-plugin-group-icons 这个插件才有的图标。
安装依赖
pnpm add vitepress-plugin-group-icons -d导入
import { defineConfig } from 'vitepress'
import { groupIconMdPlugin, groupIconVitePlugin } from 'vitepress-plugin-group-icons'
export default defineConfig({
markdown: {
config(md) {
md.use(groupIconMdPlugin)
},
},
vite: {
plugins: [
groupIconVitePlugin()
],
}
})import Theme from 'vitepress/theme'
import 'virtual:group-icons.css'
export default Theme容器样式
默认的 VitePress 容器样式过于简单,可以通过 CSS 修改一下样式
提示
提示
危险
危险
警告
警告
信息
信息
/* .vitepress/theme/style/custom-block.css */
/* 深浅色卡 */
:root {
--custom-block-info-left: #cccccc;
--custom-block-info-bg: #fafafa;
--custom-block-tip-left: #009400;
--custom-block-tip-bg: #e6f6e6;
--custom-block-warning-left: #e6a700;
--custom-block-warning-bg: #fff8e6;
--custom-block-danger-left: #e13238;
--custom-block-danger-bg: #ffebec;
--custom-block-note-left: #4cb3d4;
--custom-block-note-bg: #eef9fd;
--custom-block-important-left: #a371f7;
--custom-block-important-bg: #f4eefe;
--custom-block-caution-left: #e0575b;
--custom-block-caution-bg: #fde4e8;
}
.dark {
--custom-block-info-left: #cccccc;
--custom-block-info-bg: #474748;
--custom-block-tip-left: #009400;
--custom-block-tip-bg: #003100;
--custom-block-warning-left: #e6a700;
--custom-block-warning-bg: #4d3800;
--custom-block-danger-left: #e13238;
--custom-block-danger-bg: #4b1113;
--custom-block-note-left: #4cb3d4;
--custom-block-note-bg: #193c47;
--custom-block-important-left: #a371f7;
--custom-block-important-bg: #230555;
--custom-block-caution-left: #e0575b;
--custom-block-caution-bg: #391c22;
}
/* 标题字体大小 */
.custom-block-title {
font-size: 16px;
}
/* info容器:背景色、左侧 */
.custom-block.info {
border-left: 5px solid var(--custom-block-info-left);
background-color: var(--custom-block-info-bg);
}
/* info容器:svg图 */
.custom-block.info [class*='custom-block-title']::before {
content: '';
background-image: url("data:image/svg+xml;utf8,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24'%3E%3Cpath d='M12 22C6.477 22 2 17.523 2 12S6.477 2 12 2s10 4.477 10 10-4.477 10-10 10zm-1-11v6h2v-6h-2zm0-4v2h2V7h-2z' fill='%23ccc'/%3E%3C/svg%3E");
width: 20px;
height: 20px;
display: inline-block;
vertical-align: middle;
position: relative;
margin-right: 4px;
left: -5px;
top: -1px;
}
/* 提示容器:边框色、背景色、左侧 */
.custom-block.tip {
/* border-color: var(--custom-block-tip); */
border-left: 5px solid var(--custom-block-tip-left);
background-color: var(--custom-block-tip-bg);
}
/* 提示容器:svg图 */
.custom-block.tip [class*='custom-block-title']::before {
content: '';
background-image: url("data:image/svg+xml;utf8,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24'%3E%3Cpath fill='%23009400' d='M7.941 18c-.297-1.273-1.637-2.314-2.187-3a8 8 0 1 1 12.49.002c-.55.685-1.888 1.726-2.185 2.998H7.94zM16 20v1a2 2 0 0 1-2 2h-4a2 2 0 0 1-2-2v-1h8zm-3-9.995V6l-4.5 6.005H11v4l4.5-6H13z'/%3E%3C/svg%3E");
width: 20px;
height: 20px;
display: inline-block;
vertical-align: middle;
position: relative;
margin-right: 4px;
left: -5px;
top: -2px;
}
.custom-block.tip code {
color: #037a03;
background-color: #c3eec3;
}
/* 警告容器:背景色、左侧 */
.custom-block.warning {
border-left: 5px solid var(--custom-block-warning-left);
background-color: var(--custom-block-warning-bg);
}
/* 警告容器:svg图 */
.custom-block.warning [class*='custom-block-title']::before {
content: '';
background-image: url("data:image/svg+xml;utf8,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 1024 1024'%3E%3Cpath d='M576.286 752.57v-95.425q0-7.031-4.771-11.802t-11.3-4.772h-96.43q-6.528 0-11.3 4.772t-4.77 11.802v95.424q0 7.031 4.77 11.803t11.3 4.77h96.43q6.528 0 11.3-4.77t4.77-11.803zm-1.005-187.836 9.04-230.524q0-6.027-5.022-9.543-6.529-5.524-12.053-5.524H456.754q-5.524 0-12.053 5.524-5.022 3.516-5.022 10.547l8.538 229.52q0 5.023 5.022 8.287t12.053 3.265h92.913q7.032 0 11.803-3.265t5.273-8.287zM568.25 95.65l385.714 707.142q17.578 31.641-1.004 63.282-8.538 14.564-23.354 23.102t-31.892 8.538H126.286q-17.076 0-31.892-8.538T71.04 866.074q-18.582-31.641-1.004-63.282L455.75 95.65q8.538-15.57 23.605-24.61T512 62t32.645 9.04 23.605 24.61z' fill='%23e6a700'/%3E%3C/svg%3E");
width: 20px;
height: 20px;
display: inline-block;
vertical-align: middle;
position: relative;
margin-right: 4px;
left: -5px;
}
/* 危险容器:背景色、左侧 */
.custom-block.danger {
border-left: 5px solid var(--custom-block-danger-left);
background-color: var(--custom-block-danger-bg);
}
/* 危险容器:svg图 */
.custom-block.danger [class*='custom-block-title']::before {
content: '';
background-image: url("data:image/svg+xml;utf8,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24'%3E%3Cpath d='M12 2c5.523 0 10 4.477 10 10v3.764a2 2 0 0 1-1.106 1.789L18 19v1a3 3 0 0 1-2.824 2.995L14.95 23a2.5 2.5 0 0 0 .044-.33L15 22.5V22a2 2 0 0 0-1.85-1.995L13 20h-2a2 2 0 0 0-1.995 1.85L9 22v.5c0 .171.017.339.05.5H9a3 3 0 0 1-3-3v-1l-2.894-1.447A2 2 0 0 1 2 15.763V12C2 6.477 6.477 2 12 2zm-4 9a2 2 0 1 0 0 4 2 2 0 0 0 0-4zm8 0a2 2 0 1 0 0 4 2 2 0 0 0 0-4z' fill='%23e13238'/%3E%3C/svg%3E");
width: 20px;
height: 20px;
display: inline-block;
vertical-align: middle;
position: relative;
margin-right: 4px;
left: -5px;
top: -1px;
}
/* 提醒容器:背景色、左侧 */
.custom-block.note {
border-left: 5px solid var(--custom-block-note-left);
background-color: var(--custom-block-note-bg);
}
/* 提醒容器:svg图 */
.custom-block.note [class*='custom-block-title']::before {
content: '';
background-image: url("data:image/svg+xml;utf8,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24'%3E%3Cpath d='M12 22C6.477 22 2 17.523 2 12S6.477 2 12 2s10 4.477 10 10-4.477 10-10 10zm-1-11v6h2v-6h-2zm0-4v2h2V7h-2z' fill='%234cb3d4'/%3E%3C/svg%3E");
width: 20px;
height: 20px;
display: inline-block;
vertical-align: middle;
position: relative;
margin-right: 4px;
left: -5px;
top: -1px;
}
/* 重要容器:背景色、左侧 */
.custom-block.important {
border-left: 5px solid var(--custom-block-important-left);
background-color: var(--custom-block-important-bg);
}
/* 重要容器:svg图 */
.custom-block.important [class*='custom-block-title']::before {
content: '';
background-image: url("data:image/svg+xml;utf8,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 1024 1024'%3E%3Cpath d='M512 981.333a84.992 84.992 0 0 1-84.907-84.906h169.814A84.992 84.992 0 0 1 512 981.333zm384-128H128v-42.666l85.333-85.334v-256A298.325 298.325 0 0 1 448 177.92V128a64 64 0 0 1 128 0v49.92a298.325 298.325 0 0 1 234.667 291.413v256L896 810.667v42.666zm-426.667-256v85.334h85.334v-85.334h-85.334zm0-256V512h85.334V341.333h-85.334z' fill='%23a371f7'/%3E%3C/svg%3E");
width: 20px;
height: 20px;
display: inline-block;
vertical-align: middle;
position: relative;
margin-right: 4px;
left: -5px;
top: -1px;
}
/* 注意容器:背景色、左侧 */
.custom-block.caution {
border-left: 5px solid var(--custom-block-caution-left);
background-color: var(--custom-block-caution-bg);
}
/* 注意容器:svg图 */
.custom-block.caution [class*='custom-block-title']::before {
content: '';
background-image: url("data:image/svg+xml;utf8,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24'%3E%3Cpath d='M12 2c5.523 0 10 4.477 10 10v3.764a2 2 0 0 1-1.106 1.789L18 19v1a3 3 0 0 1-2.824 2.995L14.95 23a2.5 2.5 0 0 0 .044-.33L15 22.5V22a2 2 0 0 0-1.85-1.995L13 20h-2a2 2 0 0 0-1.995 1.85L9 22v.5c0 .171.017.339.05.5H9a3 3 0 0 1-3-3v-1l-2.894-1.447A2 2 0 0 1 2 15.763V12C2 6.477 6.477 2 12 2zm-4 9a2 2 0 1 0 0 4 2 2 0 0 0 0-4zm8 0a2 2 0 1 0 0 4 2 2 0 0 0 0-4z' fill='%23e13238'/%3E%3C/svg%3E");
width: 20px;
height: 20px;
display: inline-block;
vertical-align: middle;
position: relative;
margin-right: 4px;
left: -5px;
top: -1px;
}参考资料
实现原理
下面这些功能已经集成在了主题中,如果你也想给你 VitePress 添加这些功能,可以参考一下如果实现。
加载进度条
想要在切换路由时,页面顶部显示加载进度条,可以使用 BProgress
安装依赖
pnpm add @bprogress/core -D导入
import { onMounted, onUnmounted } from 'vue';
import { EnhanceAppContext, inBrowser } from 'vitepress';
import DefaultTheme from 'vitepress/theme';
import { BProgress } from '@bprogress/core';
import '@bprogress/core/css';
export default {
extends: DefaultTheme,
enhanceApp({ app, router }: EnhanceAppContext) {
if (inBrowser) {
BProgress.configure({ showSpinner: false });
router.onBeforeRouteChange = () => {
BProgress.start();
};
router.onAfterRouteChange = () => {
BProgress.done();
};
}
}
};样式
:root {
--bprogress-color: var(--vp-c-brand-3);
}图片缩放
medium-zoom
medium-zoom 可以非常简单的为图片添加缩放功能,但除此之外就没有任何其他功能了。如果有更多需求建议使用 fancybox。
安装依赖
pnpm add medium-zoom -D导入
import { onMounted, watch, nextTick } from 'vue';
import { useRoute } from 'vitepress';
import DefaultTheme from 'vitepress/theme';
import mediumZoom from 'medium-zoom';
import './styles/index.less';
export default {
extends: DefaultTheme,
setup() {
const route = useRoute();
const initZoom = () => {
mediumZoom('.main img', { background: 'rgba(0,0,0,0.2)' });
};
onMounted(() => {
initZoom();
});
watch(
() => route.path,
() => nextTick(() => initZoom())
);
}
};样式
.medium-zoom-overlay {
z-index: 30;
}
.medium-zoom-image {
border-radius: 10px;
z-index: 31;
}fancybox
fancybox 是非常经典的图片查看库,支持放大、缩小、翻转和旋转等丰富的功能。
安装依赖
pnpm add @fancyapps/ui -D导入
import { nextTick } from 'vue';
import '@fancyapps/ui/dist/fancybox/fancybox.css';
// 查找图像之前最近的标题
const findNearestHeading = (imgElement) => {
// 获取 img 元素的父节点
let currentElement = imgElement;
// 循环向上查找
while (currentElement && currentElement !== document.body) {
// 在当前元素的前一个兄弟节点中查找 h1-h6 标签
let previousSibling = currentElement.previousElementSibling;
while (previousSibling) {
if (previousSibling.tagName.match(/^H[1-6]$/)) {
return previousSibling.textContent.replace(/\u200B/g, '').trim(); // 返回找到的标题内容
}
previousSibling = previousSibling.previousElementSibling;
}
// 如果没有找到,继续向上一级父节点查找
currentElement = currentElement.parentElement;
}
return '';
};
export const bindFancybox = () => {
nextTick(async () => {
const { Fancybox, PanzoomAction } = await import('@fancyapps/ui'); // 采用这种导入方式是为了避免构建报错问题
const imgs = document.querySelectorAll('.vp-doc img');
imgs.forEach((img) => {
const image = img as HTMLImageElement;
if (!image.hasAttribute('data-fancybox')) {
image.setAttribute('data-fancybox', 'gallery');
}
// 赋予 alt 属性
if (!image.hasAttribute('alt') || image.getAttribute('alt') === '') {
const heading = findNearestHeading(image);
image.setAttribute('alt', heading);
}
// 赋予 data-caption 属性以便显示图片标题
const altString = image.getAttribute('alt') || '';
image.setAttribute('data-caption', altString);
});
Fancybox.bind('[data-fancybox="gallery"]', {
Hash: false,
caption: false,
Carousel: {
Zoomable: {
Panzoom: {
clickAction: false, // 禁用单击放大
dblClickAction: PanzoomAction.IterateZoom, // 双击放大
maxScale: 2,
on: {
// 单击关闭 Fancybox
singleClick: () => {
Fancybox.close();
}
}
}
},
Toolbar: {
absolute: false,
display: {
left: ['counter'],
middle: ['zoomIn', 'zoomOut', 'toggle1to1', 'rotateCCW', 'rotateCW', 'flipX', 'flipY', 'reset'],
right: ['thumbs', 'close'] // 'autoplay' 自动播放
}
},
Thumbs: {
type: 'classic',
showOnStart: false
}
}
});
});
};
export const destroyFancybox = async () => {
const { Fancybox } = await import('@fancyapps/ui');
Fancybox.destroy();
};import { onMounted, onUnmounted } from 'vue';
import { EnhanceAppContext, inBrowser } from 'vitepress';
import DefaultTheme from 'vitepress/theme';
import { bindFancybox, destroyFancybox } from 'src/utils/fancybox';
import 'src/styles/fancybox.less';
export default {
extends: DefaultTheme,
enhanceApp({ app, router }: EnhanceAppContext) {
if (inBrowser) {
router.onBeforeRouteChange = () => {
destroyFancybox(); // 销毁图片查看器
};
router.onAfterRouteChange = () => {
bindFancybox(); // 绑定图片查看器
};
}
},
setup() {
onMounted(() => {
bindFancybox();
});
onUnmounted(() => {
destroyFancybox();
});
}
};样式
.fancybox__container {
--fancybox-backdrop-bg: none;
--f-caption-color: var(--vp-c-text-1);
--f-thumb-selected-shadow: inset 0 0 0 2px var(--vp-c-brand-1);
.f-carousel__toolbar {
--f-button-bg: none;
--f-button-hover-bg: rgba(var(--vp-c-bg-reverse-rgb), 0.1);
--f-button-color: rgba(var(--vp-c-bg-reverse-rgb), 1);
--f-button-hover-color: rgba(var(--vp-c-bg-reverse-rgb), 1);
--f-button-svg-disabled-opacity: 0.2;
background: rgba(var(--vp-c-bg-rgb), 0.2);
.f-counter {
color: var(--vp-c-text-1);
}
}
&::before {
content: '';
position: absolute;
left: 0;
top: 0;
bottom: 0;
right: 0;
background: rgba(var(--vp-c-bg-rgb), 0.5);
backdrop-filter: blur(10px);
}
.f-panzoom__content {
object-fit: initial;
border-radius: 10px;
box-shadow: 0px 0px 8px rgba(0, 0, 0, 0.2);
}
}警告
原文使用的是老版本 Fancybox v5 @fancyapps/ui@5,以上代码参考原文修改后兼容 Fancybox v6,可以直接安装最新版

