MDX 和 React
在 Markdown 中使用 JSX
Docusaurus 原生支持 MDX v1,可以直接在 Markdown 文档中编写 JSX,并渲染为 React 组件。
虽然 Docusaurus 会把 .md
和 .mdx
都解析为 MDX,但第三方工具可能会对其中一些语法的处理略有不同。 为了得到最准确的解析结果和更好的编辑器支持,推荐包含 MDX 语法的文档使用 .mdx
后缀。
Check out the MDX docs to see what other fancy stuff you can do with 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** 写在一起!
Notice how it renders both the markup from your React component and the Markdown syntax:
Docusaurus 绿 和 Facebook 蓝 是我最喜欢的颜色。
我可以把我的 JSX 和 Markdown 写在一起!
由于所有文档文件都是使用MDX解析的,任何看起来像HTML的内容实际上是JSX语法。 因此,如果您需要对组件进行内联样式处理,请遵循JSX风格,并提供样式对象。
/* 不要这么写: */
<span style="background-color: red">Foo</span>
/* 要这么写: */
<span style={{backgroundColor: 'red'}}>Foo</span>
This behavior is different from Docusaurus 1. 可以参阅 从 v1 迁移到 v2。
In addition, MDX is not 100% compatible with CommonMark. Use the MDX playground to ensure that your syntax is valid MDX.
导入组件
您还可以导入其他文件中定义的自己的组件或通过npm安装的第三方组件。
<!-- Docusaurus 主题组件 -->
import TOCInline from '@theme/TOCInline';
<!-- 外部组件 -->
import Button from '@mui/material/Button';
<!-- 自定义组件 -->
import BrowserWindow from '@site/src/components/BrowserWindow';
The @site
alias points to your website's directory, usually where the docusaurus.config.js
file is. Using an alias instead of relative paths ('../../src/components/BrowserWindow'
) saves you from updating import paths when moving files around, or when versioning docs and translating.
While declaring components within Markdown is very convenient for simple cases, it becomes hard to maintain because of limited editor support, risks of parsing errors, and low reusability. Use a separate .js
file when your component involves complex JS logic:
import React from 'react';
export default function Highlight({children, color}) {
return (
<span
style={{
backgroundColor: color,
borderRadius: '2px',
color: '#fff',
padding: '0.2rem',
}}>
{children}
</span>
);
}
import Highlight from '@site/src/components/Highlight';
<Highlight color="#25c2a0">Docusaurus 绿</Highlight>
If you use the same component across a lot of files, you don't need to import it everywhere—consider adding it to the global scope. See below
MDX 组件作用域
Apart from importing a component and exporting a component, a third way to use a component in MDX is to register it to the global scope, which will make it automatically available in every MDX file, without any import statements.
For example, given this MDX file:
- 一个
- 列表!
和一些 <Highlight>自定义标记</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. Since the swizzle CLI doesn't allow wrapping non-component files yet, you should manually create the wrapper:
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!
I can conveniently use Docusaurus green everywhere!
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.
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. Use the MDX playground to ensure that your syntax is valid 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_ 就**无法工作**了
高亮文本 但之后 Markdown 就无法工作了
这一行都用 JSX,或者在这一行开头放一点纯文本:
<span style={{color: 'red'}}>这一段全部用 JSX</span> 就不用<i>操心</i> <b>Markdown</b> 的问题了
​<span style={{color: 'red'}}>← 这是个零宽空格</span> 然后在这之后 <i>Markdown</i> <b>就可以工作了</b>
这一段全部用 JSX 就不用操心 Markdown 的问题了
← 这是个零宽空格 然后在这之后 Markdown 就可以工作了
JSX 标签里的 Markdown 永远不会工作:
- 问题
- 解决方案
<span style={{color: 'red'}}>**粗体不起作用**</span>
在 JSX 标签里用 JSX,或者把 Markdown 放 到外层去:
<span style={{color: 'red'}}><b>粗体现在起作用了</b></span>
**<span style={{color: 'red'}}>粗体现在起作用了</span>**
粗体现在起作用了
在一个 JSX 标签正下方的文本会被当作 JSX 文本:
- 问题
- 解决方案
<div style={{color: 'red'}}>
**粗体还是不起作用**
</div>
粗体还是不起作用
加一个空行:
<div style={{color: 'red'}}>
**粗体现在起作用了**
</div>
粗体现在起作用了
被缩进四格的 Markdown 文本会被当作代码块:
- 问题
- 解决方案
<div style={{color: 'red'}}>
你可能以为我只是一些文本……
</div>
你可能以为我只是一些文本……
不要缩进:
<div style={{color: 'red'}}>
现在我真的只是文本了
</div>
现在我真的只是文本了
Importing code snippets
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
- Yarn
- pnpm
npm install --save raw-loader
yarn add raw-loader
pnpm add raw-loader
Now you can import code snippets from another file as it is:
import CodeBlock from '@theme/CodeBlock';
import MyComponentSource from '!!raw-loader!./myComponent';
<CodeBlock language="jsx">{MyComponentSource}</CodeBlock>
/**
* 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.
This feature is experimental and might be subject to breaking API changes in the future.
Importing 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.
<span>Hello {props.name}</span>
This is text some content from `_markdown-partial-example.mdx`.
import PartialExample from './_markdown-partial-example.mdx';
<PartialExample name="Sebastien" />
这是来自 _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).
Available exports
Within the MDX page, the following variables are available as globals:
frontMatter
: the front matter as a record of string keys and values;toc
: the table of contents, as a tree of headings. See also Inline TOC for a more concrete use-case.contentTitle
: the Markdown title, which is the firsth1
heading in the Markdown text. It'sundefined
if there isn't one (e.g. title specified in the front matter).
import TOCInline from '@theme/TOCInline';
import CodeBlock from '@theme/CodeBlock';
The table of contents for this page, serialized:
<CodeBlock className="language-json">{JSON.stringify(toc, null, 2)}</CodeBlock>
The front matter of this page:
<ul>
{Object.entries(frontMatter).map(([key, value]) => <li key={key}><b>{key}</b>: {value}</li>)}
</ul>
<p>The title of this page is: <b>{contentTitle}</b></p>
The table of contents for this page, serialized:
[
{
"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": "Importing code snippets",
"id": "importing-code-snippets",
"level": 2
},
{
"value": "Importing Markdown",
"id": "importing-markdown",
"level": 2
},
{
"value": "Available exports",
"id": "available-exports",
"level": 2
}
]
The front matter of this page:
- id: react
- description: 得益于 MDX,你可以在 Docusaurus Markdown 文档中使用 React
- slug: /markdown-features/react
The title of this page is: MDX 和 React