Skip to navigation
5-7 minutes read
By John Otander, Titus Wormer

MDX 是什么?

本文解释了 MDX 格式是什么。 它展示了如何让 markdown、JSX、JavaScript 表达式以及 ESM 中 importexport 语句在 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).

组件时代的 markdown

MDX 赋予你在 markdown 内容当中使用 JSX 的能力。 你可以导入(import)如见,例如交互式图标或警告框,并将它们嵌入 到你所书写的内容当中。 这让利用组件来编写较长的内容成为了一场革命。 🚀

实际上,MDX 可以看作是一种融合了 markdown 和 JSX 的格式,就像下面这个示例:

MDX
# 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 (importexport) 标准的支持。

MDX 语法

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

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:

HTML
<blockquote>
  <p>A blockquote with <em>some</em> emphasis.</p>
</blockquote>

You can write the equivalent in markdown (or MDX) like so:

Markdown
> A blockquote with *some* emphasis.

MDX supports standard markdown by default (CommonMark):

Markdown
# 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:

  • Indented code does not work in MDX:
    MDX
        console.log(1) // this is a paragraph in MDX!
    
    The reason for that is so you can nicely indent your components:
    MDX
    <main>
      <article>
        # Hello!
      </article>
    </main>
    
  • Autolinks do not work in MDX. The reason is that they can be indistinguishable from JSX (for example: <svg:rect>) and we prefer being explicit. If you want links, use full links: [descriptive text](https://and-the-link-here.com)
  • HTML syntax doesn’t work in MDX as it’s replaced by JSX (<img> to <img />). Instead of HTML comments, you can use JavaScript comments in braces: {/* comment! */}
  • Unescaped left angle bracket / less than (<) and left curly brace ({) have to be escaped: \< or \{ (or use expressions: {'<'}, {'{'})

More on how MDX differs from markdown is documented here.

JSX

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:

HTML
<h2>Hello, Venus!</h2>
<h2>Hello, Mars!</h2>

…to JSX (or MDX) like this:

MDX
<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:

MDX
<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):

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.

Expressions

MDX also supports JavaScript expressions inside curly braces:

MDX
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:

MDX
{(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:

MDX
{/* A comment! */}

ESM

MDX supports import and export statements from JavaScript as well. These ESM features can be used within MDX to define things:

MDX
import {External} from './some/place.js'

export const Local = props => <span style={{color: 'red'}} {...props} />

An <External>external</External> component and a <Local>local one</Local>.

ESM can also be used for non-components (data):

MDX
import {Chart} from './chart.js'
import population from './population.js'
export const pi = 3.14

<Chart data={population} label={'Something with ' + pi} />

Interleaving

You can use markdown “inlines” but not “blocks” inside JSX if the text and tags are on the same line:

MDX
<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:

MDX
<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):

MDX
<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 tags are on different lines:

MDX
Welcome! <a href="about.html">

This is home of...

# The Falcons!</a>

That’s because 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 stray closing a tag in the heading.

Further reading