在鸽了一段时间之后,终于又打算重新拾起这个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
1
---
2
title: markmap示例
3
---
4
5
## Links
6
7
- [Website](https://markmap.js.org/)
8
- [GitHub](https://github.com/gera2ld/markmap)
9
10
## Related Projects
11
12
- [coc-markmap](https://github.com/gera2ld/coc-markmap) for Neovim
13
- [markmap-vscode](https://marketplace.visualstudio.com/items?itemName=gera2ld.markmap-vscode) for VSCode
14
- [eaf-markmap](https://github.com/emacs-eaf/eaf-markmap) for Emacs
15
16
## Features
17
18
Note that if blocks and lists appear at the same level, the lists will be ignored.
19
20
### Lists
21
22
- **strong** ~~del~~ *italic* ==highlight==
23
- \`inline code\`
24
- [x] checkbox
25
- Katex: $x = {-b \pm \sqrt{b^2-4ac} \over 2a}$ <!-- markmap: fold -->
26
- [More Katex Examples](#?d=gist:af76a4c245b302206b16aec503dbe07b:katex.md)
27
- Now we can wrap very very very very long text based on \`maxWidth\` option
28
29
### Blocks
30
31
```js
32
console.log('hello, JavaScript')
33
```
34
35
| Products | Price |
36
|-|-|
37
| Apple | 4 |
38
| Banana | 2 |
39
40
![](/icons/markmap.png)
src/content/config.ts
1
const markmaps = defineCollection({
2
schema: z.object({
3
title: z.string().optional(),
4
}),
5
});
6
export const collections = { markmaps };

slug绑定思维导图

src/pages/markmaps/[markmapName].astro
1
---
2
import { type CollectionEntry ,getCollection } from 'astro:content';
3
import { Transformer } from 'markmap-lib';
4
import { fillTemplate } from 'markmap-render';
5
//slug绑定思维导图
6
export async function getStaticPaths() {
7
const markmaps = await getCollection('markmaps')
8
return markmaps.map((markmap) => ({
9
params: { markmapName: markmap.slug },
10
props: markmap,
11
}));
12
}
13
//astro props
14
type Props = CollectionEntry<'markmaps'>
15
const {markmapName} = Astro.params;
16
const markmap = Astro.props;
17
//生成渲染后的html
18
const transformer = new Transformer();
19
const { root, features } = transformer.transform(markmap.body);
20
const assets = transformer.getUsedAssets(features);
21
const html = fillTemplate(root, assets);
22
---
23
<html set:html={html}></html>
24
25
<title>{markmap.data.title || markmapName.toLocaleUpperCase()}</title>
26
<script is:inline>
27
//所有a标签改为新标签页打开,防止iframe嵌套时跳转异常
28
window.addEventListener("DOMContentLoaded",()=>document.querySelectorAll("a").forEach(a=>a.target="_blank"))
29
</script>

iframe实现Component

src/components/Markmap.astro
1
---
2
interface Props {
3
name: string;
4
height: string;
5
}
6
const { name, height } = Astro.props
7
const title = (await getCollection('markmaps')).find(m=>m.slug==name)?.data?.title
8
---
9
10
<div class="markmap-block">
11
<div class="markmap-block-toolbar">
12
<div class="markmap-name">{ title||name }</div>
13
<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>
14
<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>
15
</div>
16
<iframe class="markmap" style={`height:${height||'unset'}`} src={`/markmaps/${name}/`} loading="lazy"></iframe>
17
</div>
18
19
<style lang="scss" is:global>
20
.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;}}}
21
</style>

在mdx中使用

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

效果

markmap示例
在新标签页打开