跳到主要内容

MDX 插件

有时,您可能想要扩展或调整 Markdown 语法。例如:

  • 如何使用图像语法嵌入 YouTube 视频(![](https://youtu.be/yKNxeF4KMsY))?
  • 如何为单独一行的链接设置不同的样式,例如作为社交卡片?
  • 如何让每个页面都以版权声明开头?

答案是:创建一个 MDX 插件!MDX 有一个内置的插件系统,可用于自定义 Markdown 文件解析和转换为 JSX 的方式。MDX 插件有三种典型用例:

  • 使用现有的 remark 插件rehype 插件
  • 创建 remark/rehype 插件以转换现有 MDX 语法生成的元素;
  • 创建 remark/rehype 插件以向 MDX 引入新语法。

如果您使用 MDX 操场,您会注意到 MDX 转译有两个中间步骤:Markdown AST(MDAST)和超文本 AST(HAST),然后到最终的 JSX 输出。MDX 插件也有两种形式:

  • Remark:处理 Markdown AST。
  • Rehype:处理超文本 AST。
提示

使用插件为项目中最常用的 JSX 元素引入更短的语法。我们提供的提示语法也是由 Remark 插件生成的,您可以为自己的用例做同样的事情。

默认插件

Docusaurus 在 Markdown 处理期间注入一些默认的 Remark 插件。这些插件会:

  • 生成目录;
  • 为每个标题添加锚链接;
  • 将图像和链接转换为 require() 调用。
  • ……

这些都是 Remark 插件的典型用例,如果您想实现自己的插件,也可以作为灵感来源。

安装插件

MDX 插件通常是一个 npm 包,因此您可以像安装其他 npm 包一样使用 npm 安装它们。以数学插件为例。

npm install --save remark-math@5 rehype-katex@6
remark-mathrehype-katex 有什么不同?

如果您想知道 Remark 和 Rehype 有什么不同,这里有一个很好的例子。remark-math 在 Markdown AST 上操作,它看到 $...$ 这样的文本,所做的只是将其转换为 JSX <span class="math math-inline">...</span>,而不对内容做太多处理。这将数学公式的提取与其渲染解耦,这意味着您可以通过替换 Rehype 插件来将 KaTeX\KaTeX 替换为其他数学渲染器,如 MathJax(使用 rehype-mathjax)。

接下来,rehype-katex 在超文本 AST 上操作,此时所有内容已经转换为类似 HTML 的标签。它遍历所有带有 math 类的元素,并使用 KaTeX\KaTeX 解析和渲染内容为实际的 HTML。

注意

许多官方 Remark/Rehype 插件是仅限 ES 模块的 JavaScript 模块系统,Docusaurus 支持这种系统。我们建议使用ES 模块配置文件,以便更容易导入此类包。

接下来,导入您的插件,并通过 docusaurus.config.js 中的插件或预设配置添加它们:

docusaurus.config.js
import remarkMath from 'remark-math';
import rehypeKatex from 'rehype-katex';

export default {
presets: [
[
'@docusaurus/preset-classic',
{
docs: {
path: 'docs',
remarkPlugins: [remarkMath],
rehypePlugins: [rehypeKatex],
},
},
],
],
};
使用 CommonJS 配置文件?

如果您决定使用 CommonJS 配置文件,可以通过动态导入和异步配置创建器函数来加载这些 ES 模块插件:

docusaurus.config.js
module.exports = async function createConfigAsync() {
return {
presets: [
[
'@docusaurus/preset-classic',
{
docs: {
path: 'docs',
remarkPlugins: [(await import('remark-math')).default],
rehypePlugins: [(await import('rehype-katex')).default],
},
},
],
],
};
};

配置插件

某些插件可以配置并接受自己的选项。在这种情况下,使用 [plugin, pluginOptions] 语法,如下所示:

docusaurus.config.js
import rehypeKatex from 'rehype-katex';

export default {
presets: [
[
'@docusaurus/preset-classic',
{
docs: {
rehypePlugins: [
[rehypeKatex, {strict: false}],
],
},
},
],
],
};

您应该查看插件的文档以了解它支持的选项。

创建新的 rehype/remark 插件

如果没有现有的包满足您的自定义需求,您可以创建自己的 MDX 插件。

备注

下面的说明不是创建插件的全面指南,只是说明如何使其与 Docusaurus 一起工作。请访问 RemarkRehype 文档,以获取关于它们如何工作的更深入解释。

例如,让我们制作一个插件,访问每个 h2 标题并添加 Section X. 前缀。首先,在任何地方创建您的插件源文件 - 您甚至可以将其发布为单独的 npm 包并像上面解释的那样安装。我们会将其放在 src/remark/section-prefix.js。Remark/Rehype 插件只是一个接收 options 并返回在 AST 上操作的 transformer 的函数。

import {visit} from 'unist-util-visit';

const plugin = (options) => {
const transformer = async (ast) => {
let number = 1;
visit(ast, 'heading', (node) => {
if (node.depth === 2 && node.children.length > 0) {
node.children.unshift({
type: 'text',
value: `Section ${number}. `,
});
number++;
}
});
};
return transformer;
};

export default plugin;

现在,您可以在 docusaurus.config.js 中导入您的插件,并像使用已安装的插件一样使用它!

docusaurus.config.js
import sectionPrefix from './src/remark/section-prefix';

export default {
presets: [
[
'@docusaurus/preset-classic',
{
docs: {
remarkPlugins: [sectionPrefix],
},
},
],
],
};
提示

The transformer has a second parameter vfile which is useful if you need to access the current Markdown file's path.

const plugin = (options) => {
const transformer = async (ast, vfile) => {
ast.children.unshift({
type: 'text',
value: `The current file path is ${vfile.path}`,
});
};
return transformer;
};

Our transformImage plugin uses this parameter, for example, to transform relative image references to require() calls.

备注

The default plugins of Docusaurus would operate before the custom remark plugins, and that means the images or links have been converted to JSX with require() calls already. For example, in the example above, the table of contents generated is still the same even when all h2 headings are now prefixed by Section X., because the TOC-generating plugin is called before our custom plugin. If you need to process the MDAST before the default plugins do, use the beforeDefaultRemarkPlugins and beforeDefaultRehypePlugins.

docusaurus.config.js
export default {
presets: [
[
'@docusaurus/preset-classic',
{
docs: {
beforeDefaultRemarkPlugins: [sectionPrefix],
},
},
],
],
};

This would make the table of contents generated contain the Section X. prefix as well.