← 返回文章列表

MDX 初探:让 Markdown 拥有组件的力量

前端
  • # astro
  • # mdx
  • # markdown

背景

在写上一篇在 Astro 中嵌入 Excalidraw 交互画布时,我第一次接触了 MDX——文章后缀从 .md 变成了 .mdx。当时只是照着文档把组件嵌入进去,没细想背后的原理。这篇文章记录一下对 MDX 的初步了解。

MDX 是什么

MDX = Markdown + JSX

普通 Markdown 能写标题、列表、代码块这些格式化内容,但一旦需要嵌入交互组件(比如一个按钮、一个图表、一个画板),Markdown 就无能为力了。

MDX 在 Markdown 的基础上加入了 JSX 语法,让你可以在文章中直接导入和使用 React 组件:

import MyChart from '../components/MyChart.tsx'

## 数据趋势

下面是本站访问量的变化趋势:

<MyChart data={visitData} />

就这么简单——import 组件,像写 JSX 一样使用它。

为什么需要 MDX

普通 Markdown 的局限

Markdown 的设计目标是纯文本写作,它擅长格式化内容,但不擅长:

  • 嵌入交互式组件(按钮、表单、图表)
  • 复用 UI 组件(一个「提示框」要在多篇文章里重复写 HTML)
  • 传递动态数据

MDX 解决了什么

能力MarkdownMDX
基础格式(标题、列表、代码块)
嵌入 React/Vue 组件
使用 props 传参
import/export 语句
条件渲染
与设计系统整合

简单说:MDX 让 Markdown 从「静态文档」变成了「可编程页面」

语法差异

MDX 和 Markdown 绠一的部分完全一样,区别在于可以写 JSX:

导入组件

import Button from '../components/Button.astro'

使用组件

<Button>点我</Button>

传递 Props

<Counter initial={42} />

导出变量

MDX 文件可以 export 变量,供组件使用:

export const data = [1, 2, 3]

<Chart values={data} />

JSX 表达式

可以在花括号中使用 JavaScript 表达式:

今年是 {new Date().getFullYear()} 年

{2 * 21 === 42 && <span>计算正确</span>}

在 Astro 中使用 MDX

Astro 通过 @astrojs/mdx 集成支持 MDX。本项目的配置:

// astro.config.ts
import mdx from '@astrojs/mdx'

export default defineConfig({
  integrations: [mdx()],
})

内容集合(Content Collections)中,.md.mdx 文件会被同等对待:

// src/content.config.ts
const posts = defineCollection({
  loader: glob({ pattern: '**/*.{md,mdx}', base: './src/content/posts' }),
})

只需将文章文件从 .md 改为 .mdx,就可以在文章中使用组件了。不需要改任何路由或构建配置。

实际案例

上一篇 Excalidraw 文章就是典型的 MDX 用法:

---
title: '在 Astro 中嵌入 Excalidraw 交互画布'
---

import ExcalidrawWrapper from '@hokkeung/astro-excalidraw-editable'
import demoData from '../../excalidraw/demo.excalidraw.json'

## 效果展示

<ExcalidrawWrapper initialData={demoData} dataPath="src/content/excalidraw/demo.excalidraw.json" />

这段代码做了三件事:

  1. 导入 Astro 组件ExcalidrawWrapper 是一个封装了 React Excalidraw 的 Astro 组件
  2. 导入 JSON 数据:画布数据直接作为静态资源导入
  3. 将数据传入组件:通过 initialData prop 传递

如果只用普通 Markdown,这些都不可能做到——最多贴一张 Excalidraw 的导出截图。

注意事项

  • 不要滥用:如果文章只是纯文字内容,用 .md 就够了。MDX 带来了灵活性,但也增加了复杂度
  • SSR 兼容性:某些组件(如 Excalidraw)不能在 SSR 环境运行,需要使用 client:only 指令
  • 构建速度:MDX 需要额外的编译步骤,大量 MDX 文件可能略微影响构建速度
  • 语法冲突:MDX 中 JSX 语法和 Markdown 语法偶尔会冲突(比如 < 可能被当作 HTML 标签解析),需要用空行或 {/* */} 注释来分隔