跳到主要内容

MDX 与 React

道格龙(Docusaurus)内置了对 MDX 的支持,它允许你在 Markdown 文件中编写 JSX,并将其渲染为 React 组件。

请查阅 MDX 官方文档,看看你能用 MDX 做哪些酷炫的事情。

调试 MDX

MDX 的格式相当严格,你可能会遇到编译错误。

请使用 MDX playground 来调试它们,确保你的语法是有效的。

信息

最流行的格式化工具 Prettier 只支持旧版的 MDX v1。如果你遇到了非预期的格式化结果,你可能需要在有问题的区域前添加 {/* prettier-ignore */},或者将 *.mdx 添加到你的 .prettierignore 文件中,直到 Prettier 正式支持 MDX v3。 MDX 的一位主要作者推荐使用 remark-cli 搭配 remark-mdx

导出组件

要在 MDX 文件中定义任何自定义组件,你必须将其导出:只有以 export 开头的段落才会被解析为组件,而不是普通文本。

export const Highlight = ({children, color}) => (
<span
style={{
backgroundColor: color,
borderRadius: '2px',
color: '#fff',
padding: '0.2rem',
}}>
{children}
</span>
);

<Highlight color="#25c2a0">道格龙(Docusaurus)绿</Highlight><Highlight color="#1877F2">Facebook 蓝</Highlight> 是我最喜欢的颜色。

我可以在我的 _JSX_ 旁边编写 **Markdown**

注意看,它既渲染了来自你 React 组件的标记,也渲染了 Markdown 语法:

http://localhost:3000
道格龙(Docusaurus)绿 Facebook 蓝 是我最喜欢的颜色。

我可以在我的 JSX 旁边编写 Markdown

MDX 即 JSX

由于所有的文档文件都使用 MDX 解析,任何看起来像 HTML 的东西实际上都是 JSX。因此,如果你需要为组件内联样式,请遵循 JSX 的风格,提供一个样式对象。

/* 不要这样写: */
<span style="background-color: red">Foo</span>
/* 应该这样写: */
<span style={{backgroundColor: 'red'}}>Foo</span>

导入组件

你也可以导入在其他文件中定义的你自己的组件,或是通过 npm 安装的第三方组件。

<!-- 道格龙(Docusaurus)主题组件 -->
import TOCInline from '@theme/TOCInline';
<!-- 外部组件 -->
import Button from '@mui/material/Button';
<!-- 自定义组件 -->
import BrowserWindow from '@site/src/components/BrowserWindow';
提示

@site 别名指向你的网站目录,通常是 docusaurus.config.js 文件所在的位置。使用别名而不是相对路径('../../src/components/BrowserWindow'),可以让你在移动文件、对文档进行版本控制翻译时,不必更新导入路径。

虽然在 Markdown 中声明组件对于简单情况非常方便,但由于编辑器支持有限、存在解析错误的风险以及可复用性低,维护起来会变得困难。当你的组件涉及复杂的 JS 逻辑时,请使用一个单独的 .js 文件:

src/components/Highlight.js
import React from 'react';

// 这是一个标准的 React 函数组件
// 它接收 属性(props) (children 和 color)
// 并返回一个带样式的 <span> 元素
export default function Highlight({children, color}) {
return (
<span
style={{
backgroundColor: color,
borderRadius: '2px',
color: '#fff',
padding: '0.2rem',
}}>
{children}
</span>
);
}
markdown-file.mdx
import Highlight from '@site/src/components/Highlight';

<Highlight color="#25c2a0">道格龙(Docusaurus)绿</Highlight>
提示

如果你在大量文件中使用同一个组件,你不需要在每个地方都导入它——可以考虑将其添加到全局作用域中。详见下文

MDX 组件作用域

除了导入组件导出组件,在 MDX 中使用组件的第三种方法是将其注册到全局作用域,这将使其在每个 MDX 文件中自动可用,无需任何 import 语句。

例如,对于这个 MDX 文件:

- 一个
- 列表!

还有一些 <Highlight>自定义标记</Highlight>...

它将被编译成一个包含 ullipHighlight 元素的 React 组件。Highlight 不是一个原生的 HTML 元素:你需要为它提供你自己的 React 组件实现。

在道格龙(Docusaurus)中,MDX 组件作用域由 @theme/MDXComponents 文件提供。与 @theme/ 别名下的大多数其他导出不同,它本身不是一个 React 组件:它是一个从标签名(如 Highlight)到其 React 组件实现的记录。

如果你 swizzle弹出 这个组件,你会找到所有已实现的标签,你还可以通过 swizzle 相应的子组件来进一步定制我们的实现,比如 @theme/MDXComponents/Code(用于渲染 Markdown 代码块)。

如果你想注册额外的标签名(如上面的 <Highlight> 标签),你应该考虑包装 @theme/MDXComponents,这样你就不必维护所有现有的映射。由于 swizzle CLI 尚不支持包装非组件文件,你应该手动创建这个包装器:

src/theme/MDXComponents.js
import React from 'react';
// 导入原始的主题组件映射
import MDXComponents from '@theme-original/MDXComponents';
// 导入你的自定义高亮组件
import Highlight from '@site/src/components/Highlight';

export default {
// 复用默认的组件映射
...MDXComponents,
// 将 "<Highlight>" 标签映射到我们的 Highlight 组件
// 在 MDX 中传递给 `<Highlight>` 的所有属性(props)都会被传递给这个组件
Highlight,
};

现在,你可以在每个页面中自由使用 <Highlight>,而无需编写导入语句:

我可以方便地在任何地方使用 <Highlight color="#25c2a0">道格龙(Docusaurus)绿</Highlight>
http://localhost:3000

我可以方便地在任何地方使用 道格龙(Docusaurus)绿

注意

我们特意使用大写的标签名,如 Highlight

从 MDX v3+(即 道格龙(Docusaurus) v3+)开始,小写的标签名总是被渲染为原生的 HTML 元素,并且不会使用你提供的任何组件映射。

注意

此功能由 MDXProvider 提供支持。如果你在 React 页面中导入 Markdown,你必须通过 MDXContent 主题组件自己提供这个 provider。

src/pages/index.js
import React from 'react';
import FeatureDisplay from './_featureDisplay.mdx';
// 导入 MDXContent 组件,它提供了全局 MDX 组件作用域
import MDXContent from '@theme/MDXContent';

export default function LandingPage() {
return (
<div>
{/* 必须用 MDXContent 包裹从 .mdx 文件导入的内容 */}
{/* 这样,像 <Highlight> 这样的全局组件才能被正确识别和渲染 */}
<MDXContent>
<FeatureDisplay />
</MDXContent>
</div>
);
}

如果你没有用 MDXContent 包裹你导入的 MDX,全局作用域将不可用。

Markdown 和 JSX 的互操作性

道格龙(Docusaurus) v3 正在使用 MDX v3

MDX 语法CommonMark 大部分兼容,但更为严格,因为你的 .mdx 文件可以使用 JSX 并被编译成真正的 React 组件(请查看 playground)。

一些有效的 CommonMark 功能在 MDX 中将无法工作(更多信息),特别是:

  • 缩进代码块:请改用三个反引号
  • 自动链接 (<http://localhost:3000>):请改用常规链接语法 ([http://localhost:3000](http://localhost:3000))
  • HTML 语法 (<p style="color: red;">):请改用 JSX (<p style={{color: 'red'}}>)
  • 未转义的 {<:请改用 \ 进行转义 (\{\<)
CommonMark 支持尚处实验阶段

道格龙(Docusaurus) v3 允许通过以下选项,选择使用一种不那么严格、符合标准的 CommonMark 支持:

  • mdx.format: md 头部元数据
  • .md 文件扩展名结合 siteConfig.markdown.format: "detect" 配置

此功能处于实验性阶段,目前存在一些局限性

导入代码片段

你不仅可以导入包含组件定义的文件,还可以借助 Webpack raw-loader 将任何代码文件作为原始文本导入,然后插入到代码块中。为了使用 raw-loader,你首先需要在你的项目中安装它:

npm install --save raw-loader

现在你可以按原样从另一个文件导入代码片段:

myMarkdownFile.mdx
import CodeBlock from '@theme/CodeBlock';
import MyComponentSource from '!!raw-loader!./myComponent';

<CodeBlock language="jsx">{MyComponentSource}</CodeBlock>
http://localhost:3000
/**
* Copyright (c) Facebook, Inc. and its affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/

import React, {useState} from 'react';

export default function MyComponent() {
const [bool, setBool] = useState(false);
return (
<div>
<p>MyComponent rendered !</p>
<p>bool={bool ? 'true' : 'false'}</p>
<p>
<button onClick={() => setBool((b) => !b)}>toggle bool</button>
</p>
</div>
);
}

关于 <CodeBlock> 组件的更多细节,请参见在 JSX 中使用代码块

备注

你必须使用 <CodeBlock> 而不是 Markdown 的三反引号 ```,因为后者会按原样输出其所有内容,但在这里你想插入的是导入的文本。

注意

此功能是实验性的,未来可能会有破坏性的 API 变更。

导入 Markdown

你可以将 Markdown 文件当作组件使用,并在其他地方导入它们,无论是在 Markdown 文件还是在 React 页面中。每个 MDX 文件都默认将其页面内容导出一个 React 组件。在 import 语句中,你可以用任何名称来默认导入这个组件,但必须遵循 React 的大写命名规则。

按照惯例,使用 _ 文件名前缀将不会创建任何文档页面,并意味着该 Markdown 文件是一个**"局部文件(partial)"**,供其他文件导入。

_markdown-partial-example.mdx
<span>你好 {props.name}</span>

这是来自 `_markdown-partial-example.mdx` 的一些文本内容。
someOtherDoc.mdx
import PartialExample from './_markdown-partial-example.mdx';

<PartialExample name="Sebastien" />
http://localhost:3000
Hello Sebastien

This is text some content from _markdown-partial-example.md. Available document ids are:

  • advanced/architecture
  • advanced/client
  • advanced/index
  • advanced/plugins
  • advanced/routing
  • advanced/ssg
  • api/docusaurus.config.js
  • api/misc/create-docusaurus
  • api/misc/eslint-plugin/README
  • api/misc/eslint-plugin/no-html-links
  • api/misc/eslint-plugin/no-untranslated-text
  • api/misc/eslint-plugin/prefer-docusaurus-heading
  • api/misc/eslint-plugin/string-literal-i18n-messages
  • api/misc/logger/logger
  • api/plugin-methods/README
  • api/plugin-methods/extend-infrastructure
  • api/plugin-methods/i18n-lifecycles
  • api/plugin-methods/lifecycle-apis
  • api/plugin-methods/static-methods
  • api/plugins/plugin-client-redirects
  • api/plugins/plugin-content-blog
  • api/plugins/plugin-content-docs
  • api/plugins/plugin-content-pages
  • api/plugins/plugin-css-cascade-layers
  • api/plugins/plugin-debug
  • api/plugins/plugin-google-analytics
  • api/plugins/plugin-google-gtag
  • api/plugins/plugin-google-tag-manager
  • api/plugins/plugin-ideal-image
  • api/plugins/plugin-pwa
  • api/plugins/plugin-rsdoctor
  • api/plugins/plugin-sitemap
  • api/plugins/plugin-svgr
  • api/plugins/plugin-vercel-analytics
  • api/plugins/plugins-overview
  • api/themes/theme-classic
  • api/themes/theme-configuration
  • api/themes/theme-live-codeblock
  • api/themes/theme-mermaid
  • api/themes/theme-search-algolia
  • api/themes/themes-overview
  • blog
  • browser-support
  • cli
  • configuration
  • deployment
  • docusaurus-core
  • guides/creating-pages
  • guides/docs/create-doc
  • guides/docs/introduction
  • guides/docs/multi-instance
  • guides/docs/sidebar/autogenerated
  • guides/docs/sidebar/index
  • guides/docs/sidebar/items
  • guides/docs/sidebar/multiple-sidebars
  • guides/docs/versioning
  • guides/markdown-features/admonitions
  • guides/markdown-features/assets
  • guides/markdown-features/code-blocks
  • guides/markdown-features/diagrams
  • guides/markdown-features/head-metadata
  • guides/markdown-features/introduction
  • guides/markdown-features/links
  • guides/markdown-features/math-equations
  • guides/markdown-features/plugins
  • guides/markdown-features/react
  • guides/markdown-features/tabs
  • guides/markdown-features/toc
  • guides/whats-next
  • i18n/crowdin
  • i18n/git
  • i18n/introduction
  • i18n/tutorial
  • installation
  • introduction
  • migration/index
  • migration/v2/migration-automated
  • migration/v2/migration-manual
  • migration/v2/migration-overview
  • migration/v2/migration-translated-sites
  • migration/v2/migration-versioned-sites
  • migration/v3
  • playground
  • search
  • seo
  • static-assets
  • styling-layout
  • swizzling
  • typescript-support
  • using-plugins

通过这种方式,你可以在多个页面之间复用内容,避免材料重复。

可用导出

在 MDX 页面中,以下变量作为全局变量可用:

  • frontMatter:头部元数据,作为一个由字符串键和值组成的记录;
  • toc:目录,作为一个标题树。更具体的使用案例,请参见行内目录
  • contentTitle:Markdown 标题,即 Markdown 文本中的第一个 h1 标题。如果没有(例如,标题在头部元数据中指定),则为 undefined
import TOCInline from '@theme/TOCInline';
import CodeBlock from '@theme/CodeBlock';

此页面的目录,序列化后:

<CodeBlock className="language-json">{JSON.stringify(toc, null, 2)}</CodeBlock>

此页面的头部元数据:

<ul>
{Object.entries(frontMatter).map(([key, value]) => <li key={key}><b>{key}</b>: {value}</li>)}
</ul>

<p>此页面的标题是:<b>{contentTitle}</b></p>
http://localhost:3000

此页面的目录,序列化后:

[
{
"value": "导出组件",
"id": "exporting-components",
"level": 3
},
{
"value": "导入组件",
"id": "importing-components",
"level": 3
},
{
"value": "MDX 组件作用域",
"id": "mdx-component-scope",
"level": 3
},
{
"value": "Markdown 和 JSX 的互操作性",
"id": "markdown-and-jsx-interoperability",
"level": 3
},
{
"value": "导入代码片段",
"id": "importing-code-snippets",
"level": 2
},
{
"value": "导入 Markdown",
"id": "importing-markdown",
"level": 2
},
{
"value": "可用导出",
"id": "available-exports",
"level": 2
}
]

此页面的头部元数据:

  • id: react
  • description: 借助 MDX,在道格龙(Docusaurus)的 Markdown 文档中使用 React 的强大功能
  • slug: /markdown-features/react

此页面的标题是:MDX 与 React