中文文档 / MDX 是什么?
本文解释了什么是 MDX 格式。 它展示了如何让 markdown、JSX、JavaScript 表达式以及 ESM 中 import
和 export
语句在 MDX 中和平共处。 参见 § 入门 章节以了解如何将 MDX 集成进你的 项目中。 当你已经准备好使用 MDX 时,请参见 § 使用 MDX。
To write and enjoy MDX, you should be familiar with both markdown (see this cheat sheet and tutorial for help) and JavaScript (specifically JSX).
MDX 赋予你在 markdown 内容当中使用 JSX 的能力。 你可以导入(import)如见,例如交互式图标或警告框,并将它们嵌入 到你所书写的内容当中。 这让利用组件来编写较长的内容成为了一场革命。 🚀
实际上,MDX 可以看作是一种融合了 markdown 和 JSX 的格式,就像下面这个示例:
# Hello, world!
<div className="note">
> Some notable things in a block quote!
</div>
标题和块引用是 markdown 格式,而那些 类似 HTML 标签 的是 JSX 格式。 对于诸如强调或标题之类的常见内容,markdown 通常感觉比 HTML 或 JSX 在书写上更自然 更自然。 JSX 是对 JavaScript 的扩展,它 看起来 像 HTML,但是更便于 使用组件(或可重用的东西)。
这个实例在 <div>
上使用了 className
属性。 这是因为它是为 React 编写的,并且 React 要求将 class
书写为 className
形式。 其他框架,例如 Vue 和 Preact,则接受 class
形式。 因此,请注意 JSX 在书写上的这些不同, 因为这取决于所用的工具。
MDX 还支持 JavaScript 中的一些功能: 花括号中可以使用表达式 ({1 + 1}
) ,以及对 ESM (import
和 export
) 标准的支持。
Note: You don’t have to use this syntax with @mdx-js/*
packages. Or use it always. If you’re using a bundler integration you can change between MDX and markdown through the file extension (.mdx
vs. .md
). Alternatively, options.format
can be used.
MDX 的语法融合了 markdown 和 JSX。 This gives us something along the lines of literate programming. It also gives us an odd mix of two languages: markdown is whitespace sensitive and forgiving (what you type may not exactly work but it won’t crash) whereas JavaScript is whitespace insensitive and unforgiving (it does crash on typos).
Weirdly enough we quite like how they combine!
Markdown often feels more natural to type than HTML or JSX for common things like emphasis or headings. Markdown typically looks more like what’s intended and is terser. Instead of the following HTML:
<blockquote>
<p>A blockquote with <em>some</em> emphasis.</p>
</blockquote>
You can write the equivalent in markdown (or MDX) like so:
> A blockquote with *some* emphasis.
MDX supports standard markdown by default (CommonMark):
# Heading (rank 1)
## Heading 2
### 3
#### 4
##### 5
###### 6
> Block quote
* Unordered
* List
1. Ordered
2. List
A paragraph, introducing a thematic break:
---
```js
some.code()
```
a [link](https://example.com), an ![image](./image.png), some *emphasis*,
something **strong**, and finally a little `code()`.
Nonstandard markdown features (such as GFM, frontmatter, math, syntax highlighting) can be enabled with plugins (see ¶ Using plugins).
Some markdown features don’t work in MDX:
console.log(1) // this is a paragraph in MDX!
<main>
<article>
# Hello!
</article>
</main>
<svg:rect>
) and we prefer being explicit. If you want links, use full links: [descriptive text](https://and-the-link-here.com)
<img>
to <img />
). Instead of HTML comments, you can use JavaScript comments in braces: {/* comment! */}
<
) and left curly brace ({
) have to be escaped: \<
or \{
(or use expressions: {'<'}
, {'{'}
)More on how MDX differs from markdown is documented here.
JSX is an extension to JavaScript that looks like HTML but makes it convenient to use components (reusable things). JSX is typically combined with a frontend framework like React, Preact, or Vue. These frameworks add support for components, which let you change repeating things like the following markup:
<h2>Hello, Venus!</h2>
<h2>Hello, Mars!</h2>
…to JSX (or MDX) like this:
<Welcome name="Venus" />
<Welcome name="Mars" />
JSX is good for components. It makes repeating things more clear and allows for separation of concerns. MDX supports JSX syntax. The following looks a lot like HTML:
<h1>Heading!</h1>
<abbr title="HyperText Markup Language">HTML</abbr> is a lovely language.
<section>
And here is *markdown* in **JSX**!
</section>
But as previously mentioned you can use components too. Note that components must be defined. You can import them, define them locally, or pass them in later (see § Using MDX):
<MyComponent id="123" />
You can also use objects with components, such as the `thisOne` component on
the `myComponents` object: <myComponents.thisOne />
<Component
open
x={1}
label={'this is a string, *not* markdown!'}
icon={<Icon />}
/>
There are a few edge cases where MDX differs from JSX.
MDX also supports JavaScript expressions inside curly braces:
Two 🍰 is: {Math.PI * 2}
Expressions can contain whole JavaScript programs as long as they’re (wrapped in) an expression that evaluates to something that can be rendered. You can use an IIFE like so:
{(function () {
const guess = Math.random()
if (guess > 0.66) {
return <span style={{color: 'tomato'}}>Look at us.</span>
}
if (guess > 0.33) {
return <span style={{color: 'violet'}}>Who would have guessed?!</span>
}
return <span style={{color: 'goldenrod'}}>Not me.</span>
})()}
Expressions can be empty or contain just a comment:
{/* A comment! */}
MDX supports import
and export
statements from JavaScript as well. These ESM features can be used within MDX to define things:
import {External} from './some/place.js'
export const Local = properties => <span style={{color: 'red'}} {...properties} />
An <External>external</External> component and a <Local>local one</Local>.
ESM can also be used for non-components (data):
import {Chart} from './chart.js'
import population from './population.js'
export const pi = 3.14
<Chart data={population} label={'Something with ' + pi} />
You can use markdown “inlines” but not “blocks” inside JSX if the text and tags are on the same line:
<div># this is not a heading but *this* is emphasis</div>
Text and tags on one line don’t produce blocks so they don’t produce <p>
s either. On separate lines, they do:
<div>
This is a `p`.
</div>
We differentiate using this rule (same line or not). Not based on semantics of elements in HTML. So you can build incorrect HTML (which you shouldn’t):
<h1 className="main">
Don’t do this: it’s a `p` in an `h1`
</h1>
<h1 className="main">Do this: an `h1` with `code`</h1>
It’s not possible to wrap “blocks” if text and tags are on the same line but the corresponding opening and closing tags are in different blocks (so this is invalid!):
Welcome! <a href="about.html">
This is home of...
# The Falcons!</a>
To parse markdown, we first have to divide it into “blocks”. So in this case two paragraphs and a heading. Leaving an opening a
tag in the first paragraph and a closing a
tag in the heading. Which causes an error as it is misnested.