插件相关
让 VitePress 文档“图文并茂”:vitepress-plugin-legend
GitHub 地址:github.com/flingyp/vitepress-plugin-legend
✨ 效果预览:文档瞬间“活”起来!
先睹为快,看看这些酷炫功能:
🧠 Markmap:一键生成可交互脑图
将 Markdown 列表自动转换为可折叠的思维导图,支持缩放、拖拽和节点展开/收起:
📊 Mermaid:专业图表轻松绘制
通过简洁的语法快速生成流程图、时序图、甘特图等,并支持节点交互:
text
graph TD
A[用户登录] -->|输入密码| B{验证成功?}
B -->|是| C[进入首页]
B -->|否| D[提示错误]
C --> E[加载用户数据]
🚀 快速开始:3 分钟实现图文并茂
第一步:安装插件
bash
pnpm add vitepress-plugin-legend -D
bash
npm install vitepress-plugin-legend -D
bash
yarn add vitepress-plugin-legend -D
第二步:配置 VitePress
更新 .vitepress/config.ts
:
ts
import { defineConfig } from "vitepress";
import { vitepressPluginLegend } from "vitepress-plugin-legend";
export default defineConfig({
markdown: {
config(md) {
vitepressPluginLegend(md, {
markmap: { showToolbar: true }, // 启用脑图工具栏
mermaid: true, // 启用 Mermaid 支持
});
},
},
});
更新 .vitepress/theme/index.ts
:
ts
import type { Theme } from "vitepress";
import DefaultTheme from "vitepress/theme";
import { initComponent } from "vitepress-plugin-legend/component";
import "vitepress-plugin-legend/dist/index.css";
export default {
extends: DefaultTheme,
enhanceApp({ app }) {
initComponent(app);
},
} satisfies Theme;
第三步:在 Markdown 中使用图表
绘制脑图(Markmap):
markdown
```markmap
# 前端面试
### HTML
- 语义化标签
- SEO 优化
### CSS
- Flex 布局
- Grid 布局
### JavaScript
- 闭包
- 事件循环
```
markmap 演示效果:
绘制流程图(Mermaid):
markdown
```mermaid
sequenceDiagram
participant U as 用户
participant S as 服务器
U->>S: 请求登录
S-->>U: 返回 Token
U->>S: 携带 Token 请求数据
S-->>U: 返回用户数据
```
mermaid 演示效果:
🎭 高级用法:组件化调用
除了代码块方式,你还可以使用 Vue 组件 引入外部图表文件:
markdown
<!-- 引入外部脑图文件 -->
<PreviewMarkmapPath path="./mindmap.md" showToolbar />
<!-- 引入外部 Mermaid 文件 -->
<PreviewMermaidPath path="./flowchart.mmd" />
图片查看器 Fancybox
说明
参照维知笔记的配置教程,进行了配置,发现因为 fancybox 的版本更新,有些配置项已经失效,我又翻了 fancybox 的官网,对一些配置项进行了调整,目前已经能够使用。
安装 Fancybox
bash
pnpm install --save @fancyapps/ui
bash
npm install --save @fancyapps/ui
bash
yarn install --save @fancyapps/ui
封装组件 ImgViewer.ts
先封装一个组件 ImgViewer.ts,以便我们统一处理文章中的图片:
- 鉴于 Fancybox 的要求,不同图片的
data-fancybox
属性值将归于不同图库,所以我们对图片统一设置此属性 - 由于个人在文章图片中没有单独设置
alt
属性,所以统一设置此属性为离图片最近的标题文本
代码如下:
ts
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 } = 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, // 禁用hash导航
Carousel: {
Thumbs: {
type: "classic", // 经典缩略图,"modern" 现代缩略图
showOnStart: false, // 开始不显示缩略图列表
},
Toolbar: {
display: {
left: ["infobar"],
middle: [
"zoomIn",
"zoomOut",
"toggle1to1",
"rotateCCW",
"rotateCW",
"flipX",
"flipY",
],
right: ["slideshow", "thumbs", "close"], // 'slideshow' 自动播放
},
},
transition: "slide",
Zoomable: {
Panzoom: {
maxScale: 4, // 最大缩放比例
},
},
},
});
});
};
export const destroyFancybox = async () => {
const { Fancybox } = await import("@fancyapps/ui");
Fancybox.destroy();
};
2. 在全局设置中启用组件
我们在全局设置中根据不同生命周期和运行时 API,启用不同的组件方法。代码如下:
ts
import DefaultTheme from "vitepress/theme";
import "./style/index.css";
import { inBrowser } from "vitepress";
import { bindFancybox, destroyFancybox } from "./components/ImgViewer"; // 图片查看器
import "./style/fancybox.css";
export default {
extends: DefaultTheme,
Layout: MyComponent,
async enhanceApp({ app, router }) {
if (inBrowser) {
router.onBeforeRouteChange = () => {
destroyFancybox(); // 销毁图片查看器
};
router.onAfterRouteChanged = () => {
bindFancybox(); // 绑定图片查看器
};
}
},
setup() {
onMounted(() => {
bindFancybox();
});
onUnmounted(() => {
destroyFancybox();
});
},
};
修改默认样式
由于对 Fancybox 的纯黑背景不太感冒,修改为半透明高斯模糊遮罩层,代码如下:
css
.fancybox__container {
--fancybox-backdrop-bg: rgba(var(--vp-c-bg-rgb), 0.5);
--fancybox-bg: transparent;
}
/* 遮罩层样式:强制添加模糊和透明度(覆盖默认深黑) */
.fancybox__container .fancybox__backdrop {
/* 继承容器的背景色变量,叠加模糊 */
background: var(--fancybox-backdrop-bg);
opacity: 0.96;
backdrop-filter: blur(10px); /* 核心模糊效果 */
}
/* 工具栏样式 */
.fancybox__container .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-button-color: #374151; /* 深灰 */
}
/* 图片标题样式 */
.fancybox__container .f-caption {
color: var(--vp-c-text-1); /* 保持原主题色 */
}
/* 图片适配逻辑 */
.fancybox-image {
object-fit: initial; /* 避免图片被拉伸/压缩 */
}
/* 缩略图选中状态 */
/* 新版缩略图选中逻辑 */
.f-thumbs.is-classic .f-thumbs__slide.is-selected button:after {
border-color: var(--weiz-primary-color); /* 主题色替换 */
/* 覆盖默认选中阴影(可选,若需更明显的选中效果) */
--f-thumb-selected-shadow: inset 0 0 0 2px var(--weiz-primary-color);
}