Skip to content
欢迎扫码关注公众号

VitePress & Markmap

虽然 mermaid 也支持思维导图,但是功能较弱,而且也比较乱。还是比较喜欢 markmap 这种样式的,于是调查了下如何在 VitePress 中使用 markmap。

因为前端不是太熟,感觉写法不够优雅,不过勉强能用。

下面是具体的步骤,仅供参考。

安装 markmap-lib 依赖:

bash
npm i -D markmap-lib

config.mts 文件中添加 Markdown 插件配置:

ts
import { defineConfig } from 'vitepress'
import { Transformer } from 'markmap-lib'

const transformer = new Transformer();
function escapeHtml(unsafe) {
  return unsafe
    .replace(/&/g, "&")
    .replace(/</g, "&lt;")
    .replace(/>/g, "&gt;")
    .replace(/"/g, "&quot;")
    .replace(/'/g, "&#039;");
}
export default defineConfig({
  title: "My Awesome Project",
  description: "A VitePress Site",
  markdown: {
    config: (md) => {
      const temp = md.renderer.rules.fence.bind(md.renderer.rules);
      md.renderer.rules.fence = (tokens, idx, options, env, slf) => {
        const token = tokens[idx];
        if (token.info === 'mindmap') {
          try {
            const { root, _ } = transformer.transform(token.content.trim());
            return `<svg class="markmap-svg" data-json='${escapeHtml(JSON.stringify(root))}'></svg>`;
          } catch (ex) {
            return `<pre>${ex}</pre>`
          }
        }
        return temp(tokens, idx, options, env, slf)
      };
    }
  },
})

在自定义主题中添加渲染 Markmap 的处理:

ts
// .vitepress/theme/index.js
import { onMounted, watch, nextTick } from 'vue';
import DefaultTheme from 'vitepress/theme'
import { onContentUpdated, useRoute } from 'vitepress';
import type { Theme as ThemeConfig } from 'vitepress'
import { Markmap } from 'markmap-view'

// 引入自定义样式文件
import './custom.css'
// 渲染思维导图
function renderMindmap() {
  const mindmaps = document.querySelectorAll('.markmap-svg');
  for (const mindmap of mindmaps) {
    const dataJson = mindmap.getAttribute('data-json');
    if (mindmap instanceof SVGElement && dataJson) {
      if(mindmap.children.length > 0) continue;
      const mp = Markmap.create(mindmap, {
        autoFit: true,
        pan: false,
        zoom: false,
      }, JSON.parse(dataJson));
      // 重新设置 SVG 的高度
      setTimeout(() => {
        const width = mp.state.rect.x2 - mp.state.rect.x1; // SVG 的实际宽度
        const height = mp.state.rect.y2 - mp.state.rect.y1; // SVG 的实际高度
        const aspectRatio = height / width; // 高宽比
        // 定义一个子方法用于重新设置 mindmap 的高度
        function setMindmapHeight(mindmap: SVGElement, aspectRatio: number) {
          const realHeight = mindmap.clientWidth * aspectRatio + 30;
          mindmap.style.height = `${realHeight}px`;
          mp.fit()
        }
        // 注册 window 的窗口大小变动事件
        window.addEventListener('resize', () => {
          setMindmapHeight(mindmap, aspectRatio);
        })
        // 初始设置时调用子方法
        setMindmapHeight(mindmap, aspectRatio);
      }, 0)
    }
  }
}
export const Theme: ThemeConfig = {
  extends: DefaultTheme,
  setup() {
    onContentUpdated(() => {
      renderMindmap();
    });
  },
}

export default Theme

示例

markdown
```mindmap
# root
## child1
- child3
## child2
- child3
```
markdown
```mindmap
## Links
- <https://markmap.js.org/>
- [GitHub](https://github.com/gera2ld/markmap)
## Related
- [coc-markmap](https://github.com/gera2ld/coc-markmap)
- [gatsby-remark-markmap](https://github.com/gera2ld/gatsby-remark-markmap)
## Features
- links
- **inline** ~~text~~ *styles*
- multiline
  text
- `inline code`
- `<video>`
```

2025-06-13 追记

  • 修复了节点行内代码中包含 <> HTML 标签时无法显示的问题;
  • 增加了在页面大小变动时自动调整的处理;

参考

  1. https://github.com/NeroBlackstone/markdown-it-mindmap/