五花八门的 Markdown Admonitions
2022-07-18 · 1,184 chars · 6 min read
背景#
最近写博客的时候,感觉在文章样式上,表现力还不是特别足。例如,我想加个说明、提示、警告、引用,在 markdown 里全部都是 >
,如下:
就是 <blockquote>,块级引用元素
样式比较单调,承载太多功能,直接导致表意不明。
五花八门的 Admonitions#
为了更好的区分 Note、Tip、Warning、Blockquote,打算把 Admonitions 功能加上,或者也可以叫 Alert,就是下面这样的东西,功能已经做好了大家直接看:
Note
这是一条 Note
Tip
这是一条 Tip
Warning
这是一条 Warning
这样是不是更清晰易懂了?其实这种功能在中后台系统、文档类的站点里经常看到。
但就是这么一个简单的功能,在 markdown 里面,实现的五花八门。主要原因还是 markdown 没有明确统一的标准,变体太多,加上功能实现比较简单。例如我这里就是直接用 remark-directive,基于通用指令实现的。我们先来看看各路选手长什么样子
Github 方案#
目前 Github 实现了这样一个版本:
> **Note** > This is a note > **Warning** > This is a warning
还是基于引用来做的,效果可以看这里
微软方案#
微软的方案,语法是这样的:
> [!NOTE] > Information the user should notice even if skimming. > [!TIP] > Optional information to help a user be more successful. > [!IMPORTANT] > Essential information required for user success. > [!CAUTION] > Negative potential consequences of an action. > [!WARNING] > Dangerous certain consequences of an action.
它实现的类型还挺多,效果可以看这里,我就不截图了
加个 blockquote 小声说一下,我的样式就是“参考”的微软
MDN 方案#
MDN 语法长这样
> **Note:** This is how you write a note. > > It can have multiple lines. > **Warning:** This is how you write a warning. > > It can have multiple paragraphs.
效果可以在这里看,大家看 MDN 文档的时候应该也能看到
VuePress 方案#
VuePress 的没用过,简单看了下底层应该是 markdown-it,它的语法是这样的
::: tip 这是一个提示 ::: ::: warning 这是一个警告 ::: ::: danger 这是一个危险警告 ::: ::: details 这是一个详情块,在 IE / Edge 中不生效 :::
Docusaurus 方案#
Docusaurus 我特别喜欢,它和我的博客一样是使用 mdx 的,通过 remark 插件实现 Admonitions,不过用了一个挺老的库 remark-admonitions。
:::note 你的标题 note ::: :::tip tip ::: ::: :::danger danger :::
在这里看效果,Docusaurus 的实现非常完善,毕竟是专业的文档工具。
我的实现#
综合看下来,Github 的实现非常保守和收敛,没太多功能。微软支持的类型稍微丰富一些。MDN 的功能会更多,可以自定义标题。Docusaurus 其实是最完善的,但是插件有点老,看着好久没更新了。
我简单比较了下,还是喜欢基于通用指令的方式做,因为在内容前后分别加 :::Note
和 :::
,中间可以写任何东西,而且写起来要比 >
方便的多。
比如我可以写成这样:
这里的文案还可以自定义!!!
这是一个 Tip
- item-1:这是一个链接
- item-2:strong
- item-3:
delete
const str = 'hello world!' console.log(str)
具体的实现方案,还是基于 remark 的那一套,首先添加 remark-directive,然后是自己写的插件
{ loader: '@mdx-js/loader', // webpack 里用 mdx loader 加载 options: { remarkPlugins: [ remarkGfm, remarkMdxImages, remarkDirective, // 这是 remark-directive admonitionsPlugin, // 这是自己写的插件 ], providerImportSource: '@mdx-js/react', }, },
admonitions plugin 的代码如下:
import { visit } from 'unist-util-visit' function admonitionsPlugin() { return (tree) => { visit(tree, (node) => { if (node.type === 'containerDirective') { // 仅仅支持 Note Warning Tip if (node.name !== 'Note' && node.name !== 'Warning' && node.name !== 'Tip') { return } const data = node.data || (node.data = {}) // title 的文案优先使用自定义的 const title = node.attributes.title || node.name; data.hName = 'div' // 加两个 class,控制样式 data.hProperties = { className: `admonition admonition-${node.name.toLowerCase()}`, } // 插入 title node.children.unshift({ type: 'paragraph', data: { hProperties: { // 设置 title 的样式 className: 'admonition-title', }, }, // 插入 title 内容 children: [{ type: 'text', value: title }], }) } }) } } export default admonitionsPlugin
最终的效果是输入:
:::Note{title="这里可以添加可选的title"} 这是一条 note :::
输出:
<div class="admonition admonition-note"> <p class="admonition-title">这里可以添加可选的title</p> <p>这是一条 note</p> </div>
剩下的就是 CSS 的事情了。