跳到主要内容
版本:2.1.0

MDX 和 React

在 Markdown 中使用 JSX

Docusaurus 原生支持 MDX v1,可以直接在 Markdown 文档中编写 JSX,并渲染为 React 组件。

备注

虽然 Docusaurus 会把 .md.mdx 都解析为 MDX,但第三方工具可能会对其中一些语法的处理略有不同。 为了得到最准确的解析结果和更好的编辑器支持,推荐包含 MDX 语法的文档使用 .mdx 后缀。

可以读读 MDX 的文档,看看 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 蓝 是我最喜欢的颜色。

我可以把我的 JSXMarkdown 写在一起!

MDX 仍然是 JSX

由于所有文件都使用 MDX 解析,所以任何看起来像 HTML 的文件实际上都是 JSX。 因此,如果想使用内联样式,要使用 JSX 语法,即给 style 的值设置为 JavaScript 对象。

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

这与 Docusaurus 1 的方式不同。 可以参阅 从 v1 迁移到 v2

另外,MDX 并不 与 CommonMark 规范 100% 兼容。 使用 MDX 在线示例 来确保你的语句是可以被 MDX 正确解析的。

导入组件

你也可以导入自己在其他目录中编写的组件,或者是从 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';

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 文件中使用。

例如,在下面给定的 MDX 文件中:

- 一个
- 列表!

And some <Highlight>custom markup</Highlight>...

It will be compiled to a React component containing ul, li, p, and Highlight elements. Highlight is not a native html element: you need to provide your own React component implementation for it.

In Docusaurus, the MDX component scope is provided by the @theme/MDXComponents file. It's not a React component, per se, unlike most other exports under the @theme/ alias: it is a record from tag names like Highlight to their React component implementations.

If you swizzle this component, you will find all tags that have been implemented, and you can further customize our implementation by swizzling the respective sub-component, like @theme/MDXComponents/Code (which is used to render Markdown code blocks).

If you want to register extra tag names (like the <Highlight> tag above), you should consider wrapping @theme/MDXComponents, so you don't have to maintain all the existing mappings. 因为 swizzle CLI 还不允许包裹非组件的文件,所以你需要手动创建这个包装组件:

src/theme/MDXComponents.js
import React from 'react';
// Import the original mapper
import MDXComponents from '@theme-original/MDXComponents';
import Highlight from '@site/src/components/Highlight';

export default {
// Re-use the default mapping
...MDXComponents,
// Map the "<Highlight>" tag to our Highlight component
// `Highlight` will receive all props that were passed to `<Highlight>` in MDX
Highlight,
};

And now, you can freely use <Highlight> in every page, without writing the import statement:

I can conveniently use <Highlight color="#25c2a0">Docusaurus green</Highlight> everywhere!
http://localhost:3000

I can conveniently use Docusaurus green everywhere!

warning

We use upper-case tag names like Highlight on purpose.

From MDX v2+ onward (Docusaurus v3+), lower-case tag names are always rendered as native html elements, and will not use any component mapping you provide.

注意

This feature is powered by a wrapper provider. If you are importing Markdown in a React page, you have to supply this provider yourself through the MDXContent theme component.

src/pages/index.js
import React from 'react';
import FeatureDisplay from './_featureDisplay.mdx';
import MDXContent from '@theme/MDXContent';

export default function LandingPage() {
return (
<div>
<MDXContent>
<FeatureDisplay />
</MDXContent>
</div>
);
}

If you don't wrap your imported MDX with MDXContent, the global scope will not be available.

Markdown 和 JSX 的交互

Docusaurus v2 is using MDX v1, which has a lot of known cases where the content fails to be correctly parsed as Markdown. 使用 MDX 在线示例 来确保你的语句是可以被 MDX 正确解析的。

Samples of parsing failures

A paragraph starting with a JSX tag will be seen entirely as a JSX string:

<span style={{color: 'red'}}>高亮文本</span> 但之后_Markdown_ 就**无法工作**
http://localhost:3000
高亮文本 但之后 _Markdown_ 就**无法工作**了

JSX 标签里的 Markdown 永远不会工作:

<span style={{color: 'red'}}>**粗体不起作用**</span>
http://localhost:3000
**粗体不起作用**

在一个 JSX 标签正下方的文本会被当作 JSX 文本:

<div style={{color: 'red'}}>
**粗体还是不起作用**
</div>
http://localhost:3000
**粗体还是不起作用**

被缩进四格的 Markdown 文本会被当作代码块:

<div style={{color: 'red'}}>

你可能以为我只是一些文本……

</div>
http://localhost:3000
你可能以为我只是一些文本……

导入代码片段

You can not only import a file containing a component definition, but also import any code file as raw text, and then insert it in a code block, thanks to Webpack raw-loader. In order to use raw-loader, you first need to install it in your project:

npm install --save raw-loader

Now you can import code snippets from another file as it is:

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>
);
}

See using code blocks in JSX for more details of the <CodeBlock> component.

备注

You have to use <CodeBlock> rather than the Markdown triple-backtick ```, because the latter will ship out any of its content as-is, but you want to interpolate the imported text here.

warning

This feature is experimental and might be subject to breaking API changes in the future.

导入 Markdown

You can use Markdown files as components and import them elsewhere, either in Markdown files or in React pages.

By convention, using the _ filename prefix will not create any doc page and means the Markdown file is a "partial", to be imported by other files.

_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
你好 思达

这是来自 _markdown-partial-example.md 的一些文本。

This way, you can reuse content among multiple pages and avoid duplicating materials.

注意

Currently, the table of contents does not contain the imported Markdown headings. This is a technical limitation that we are trying to solve (issue).

全局导出项

Within the MDX page, the following variables are available as globals:

  • frontMatter:Markdown 文档的前言,包含字符串键和对应的值;
  • toc:目录,作为一个标题列表。 这个变量的实际用法可以参考内联目录
  • contentTitle:Markdown 文档标题,即文档中的第一个 h1 标题。 如果没有则是 undefined,例如把标题定义在了文档的 title 前言中。
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": "在 Markdown 中使用 JSX",
"id": "using-jsx-in-markdown",
"level": 2
},
{
"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