Astro博客中使用Markmap思维导图


发表于 修改于 前端博客构建 526 字 3 分钟

本文已过时,推荐使用 remark-markmap 插件方案

过时方案

在鸽了一段时间之后,终于又打算重新拾起这个Astro博客项目。接下来我计划将以往的笔记和博文迁移过来。在之前的Hexo项目中我使用的是hexo-markmap插件在博客中插入思维导图,现在需要在Astro中实现类似的功能。决定用markmap-render配合Astro的[slug],最后用<iframe>标签实现。

实现

安装依赖

yarn add markmap-common markmap-lib markmap-render

思维导图content

src/content/markmaps/markmap-example.md
---
title: markmap示例
---
## Links
- [Website](https://markmap.js.org/)
- [GitHub](https://github.com/gera2ld/markmap)
## Related Projects
- [coc-markmap](https://github.com/gera2ld/coc-markmap) for Neovim
- [markmap-vscode](https://marketplace.visualstudio.com/items?itemName=gera2ld.markmap-vscode) for VSCode
- [eaf-markmap](https://github.com/emacs-eaf/eaf-markmap) for Emacs
## Features
Note that if blocks and lists appear at the same level, the lists will be ignored.
### Lists
- **strong** ~~del~~ *italic* ==highlight==
- \`inline code\`
- [x] checkbox
- Katex: $x = {-b \pm \sqrt{b^2-4ac} \over 2a}$ <!-- markmap: fold -->
- [More Katex Examples](#?d=gist:af76a4c245b302206b16aec503dbe07b:katex.md)
- Now we can wrap very very very very long text based on \`maxWidth\` option
### Blocks
```js
console.log('hello, JavaScript')
```
| Products | Price |
|-|-|
| Apple | 4 |
| Banana | 2 |
![](/icons/markmap.png)
src/content/config.ts
const markmaps = defineCollection({
schema: z.object({
title: z.string().optional(),
}),
});
export const collections = { markmaps };

slug绑定思维导图

src/pages/markmaps/[markmapName].astro
---
import { type CollectionEntry ,getCollection } from 'astro:content';
import { Transformer } from 'markmap-lib';
import { fillTemplate } from 'markmap-render';
//slug绑定思维导图
export async function getStaticPaths() {
const markmaps = await getCollection('markmaps')
return markmaps.map((markmap) => ({
params: { markmapName: markmap.slug },
props: markmap,
}));
}
//astro props
type Props = CollectionEntry<'markmaps'>
const {markmapName} = Astro.params;
const markmap = Astro.props;
//生成渲染后的html
const transformer = new Transformer();
const { root, features } = transformer.transform(markmap.body);
const assets = transformer.getUsedAssets(features);
const html = fillTemplate(root, assets);
---
<html set:html={html}></html>
<title>{markmap.data.title || markmapName.toLocaleUpperCase()}</title>
<script is:inline>
//所有a标签改为新标签页打开,防止iframe嵌套时跳转异常
window.addEventListener("DOMContentLoaded",()=>document.querySelectorAll("a").forEach(a=>a.target="_blank"))
</script>

iframe实现Component

src/components/Markmap.astro
---
interface Props {
name: string;
height: string;
}
const { name, height } = Astro.props
const title = (await getCollection('markmaps')).find(m=>m.slug==name)?.data?.title
---
<div class="markmap-block">
<div class="markmap-block-toolbar">
<div class="markmap-name">{ title||name }</div>
<svg class="link-btn btn" onclick=`window.open('/markmaps/${name}/')` xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><title>在新标签页打开</title><path d="M14,3V5H17.59L7.76,14.83L9.17,16.24L19,6.41V10H21V3M19,19H5V5H12V3H5C3.89,3 3,3.9 3,5V19A2,2 0 0,0 5,21H19A2,2 0 0,0 21,19V12H19V19Z" /></svg>
<svg class="expend-btn btn" onclick="this.parentNode.nextElementSibling.hidden = !this.parentNode.nextElementSibling.hidden" viewBox="0 0 24 24"><path d="M7.41,8.58L12,13.17L16.59,8.58L18,10L12,16L6,10L7.41,8.58Z"></path></svg>
</div>
<iframe class="markmap" style={`height:${height||'unset'}`} src={`/markmaps/${name}/`} loading="lazy"></iframe>
</div>
<style lang="scss" is:global>
.markmap-block{display:flex;flex-direction:column;border-radius:0.5em;overflow:hidden;border:1.5px solid #d9d9d9;box-shadow:0.1rem 0.1rem 0.2rem #00000028;.markmap{width:100%;border:0;outline:0;}.markmap-block-toolbar{display:flex;height:2.15em;background:#eef3f4;font-size:16px;align-items:center;user-select:none;padding-left:1em;svg{fill:var(--font-color);}.markmap-name{width:100%;text-transform:uppercase;font-weight:bold;}.link-btn{height:1.2em;width:2em;display:none;}.expend-btn{height:1.4em;width:2em;padding-right:0.6em;}}&:hover{.markmap-block-toolbar .link-btn{display:unset;}}}
</style>

在mdx中使用

src/content/posts/mypost.mdx
import Markmap from '@/components/Blog/Markmap.astro';
<Markmap name="markmap-example" height="500px"/>

评论