Skip to main content

Markdown 和 MDX

Markdown 是一种轻量级标记语言,用于格式化文本。 它允许您使用纯文本语法编写,并将其转换为结构有效的 HTML。 通常用于在网站和博客上编写内容。

你可以这样写...

I **love** using [Next.js](https://nextjs.org/)

输出:

<p>I <strong>love</strong> using <a href="https://nextjs.org/">Next.js</a></p>

MDX 是 markdown 的超集, 允许您直接在 markdown 文件中编写 JSX。 这是一种在内容中添加动态交互性并嵌入 React 组件的强大方式。

Next.js 可以支持应用内的本地 MDX 内容,以及在服务器上动态获取的远程 MDX 文件。 Next.js 插件负责将 markdown 和 React 组件转换为 HTML, 包括在 App Router 中使用 Server Components(在 App Router 中为默认设置)的支持。

  • @next/mdx

@next/mdx 包用于配置 Next.js,以便处理 markdown 和 MDX。 它从本地文件中提取数据,允许您直接在 /pages/app 目录中创建扩展名为 .mdx 的页面。

让我们来看看如何配置和使用 Next.js 中的 MDX。

入门

安装用于渲染 MDX 所需的包:

npm install @next/mdx @mdx-js/loader @mdx-js/react @types/mdx

在应用程序的根目录(/appsrc 的父文件夹)创建一个 mdx-components.tsx 文件:

note

使用 App Router 与 MDX 需要 mdx-components.tsx,没有它将无法正常工作。

mdx-components.tsx
import type { MDXComponents } from 'mdx/types'

export function useMDXComponents(components: MDXComponents): MDXComponents {
return {
...components,
}
}

更新项目根目录下的 next.config.js 文件以配置其使用 MDX:

next.config.js
const withMDX = require('@next/mdx')()

/** @type {import('next').NextConfig} */
const nextConfig = {
// 配置 `pageExtensions` 以包含 MDX 文件
pageExtensions: ['js', 'jsx', 'mdx', 'ts', 'tsx'],
// 可选地,在下面添加任何其他 Next.js 配置
}

module.exports = withMDX(nextConfig)

然后,在 /app 目录中创建一个新的 MDX 页面:

your-project
├── app
│ └── my-mdx-page
│ └── page.mdx
└── package.json

现在,您可以在 MDX 页面中直接使用 markdown 并导入 React 组件:

import { MyComponent } from 'my-components'

# Welcome to my MDX page!

This is some **bold** and _italics_ text.

This is a list in markdown:

- One
- Two
- Three

Checkout my React component:

<MyComponent />

导航到 /my-mdx-page 路由应该显示您渲染的 MDX。

远程 MDX

如果您的 markdown 或 MDX 文件或内容存储在其他位置,您可以在服务器上动态获取它。这对于存储在单独的本地文件夹、CMS、数据库或其他任何地方的内容非常有用。

有两个用于获取 MDX 内容的流行社区包:

  • next-mdx-remote
  • contentlayer
note

请小心使用。MDX 编译为 JavaScript 并在服务器上执行。 您应该只从可信赖的源获取 MDX 内容,否则可能导致远程代码执行(RCE)。

以下示例使用 next-mdx-remote

app/my-mdx-page-remote/page.tsx
import { MDXRemote } from 'next-mdx-remote/rsc'

export default async function RemoteMdxPage() {
// MDX 文本 - 可以来自本地文件、数据库、CMS、fetch 等任何地方...
const res = await fetch('https://...')
const markdown = await res.text()
return <MDXRemote source={markdown} />
}

导航到 /my-mdx-page-remote 路由应该显示您渲染的 MDX。

布局

要在 MDX 页面之间共享布局,可以使用 App Router 的内置布局支持。

app/my-mdx-page/layout.tsx

export default function MdxLayout({ children }: { children: React.ReactNode }) {
// 在这里创建任何共享布局或样式
return <div style={{ color: 'blue' }}>{children}</div>
}

Remark 和 Rehype 插件

您可以选择提供 remarkrehype 插件来转换 MDX 内容。

例如,您可以使用 remark-gfm 来支持 GitHub Flavored Markdown。

由于 remarkrehype 生态系统仅支持 ESM, 您需要使用 next.config.mjs 作为配置文件。

next.config.mjs
import remarkGfm from 'remark-gfm'
import createMDX from '@next/mdx'

/** @type {import('next').NextConfig} */
const nextConfig = {
// 配置 `pageExtensions` 以包含 MDX 文件
pageExtensions: ['js', 'jsx', 'mdx', 'ts', 'tsx'],
// 可选地,在下面添加任何其他 Next.js 配置
}

const withMDX = createMDX({
// 在这里添加 markdown 插件,根据需要
options: {
remarkPlugins: [remarkGfm],
rehypePlugins: [],
},
})

// 将 MDX 配置与 Next.js 配置合并
export default withMDX(nextConfig)

Frontmatter

Frontmatter 是一种类似于 YAML 的键值对,用于存储有关页面的数据。 @next/mdx 默认不支持 frontmatter, 尽管有许多解决方案可以将 frontmatter 添加到 MDX 内容中,例如:

要使用 @next/mdx 访问页面元数据,您可以在 .mdx 文件内导出一个 metadata 对象:

export const metadata = {
author: 'John Doe',
}

# My MDX page

自定义元素

使用 markdown 的一个愉快之处是,它映射到本机 HTML 元素,使得编写快速且直观。

This is a list in markdown:

- One
- Two
- Three

上述生成以下 HTML:

<p>This is a list in markdown:</p>

<ul>
<li>One</li>
<li>Two</li>
<li>Three</li>
</ul>

当您想为网站或应用程序的自定义感觉为您自己的元素设置样式时,您可以传递短代码。 这些是映射到 HTML 元素的您自己的自定义组件。

为此,打开应用程序根目录中的 mdx-components.tsx 文件并添加自定义元素:

mdx-components.tsx
import type { MDXComponents } from 'mdx/types'
import Image from 'next/image'

// 此文件允许您提供要在 MDX 文件中使用的自定义 React 组件
// 您可以导入和使用任何您想要的 React 组件,包括内联样式、
// 来自其他库的组件等等。

export function useMDXComponents(components: MDXComponents): MDXComponents {
return {
// 允许自定义内置组件,例如添加样式。
h1: ({ children }) => <h1 style={{ fontSize: '100px' }}>{children}</h1>,
img: (props) => (
<Image
sizes="100vw"
style={{ width: '100%', height: 'auto' }}
{...props}
/>
),
...components,
}
}

深入了解:如何将 markdown 转换为 HTML?

React 不本地理解 markdown。markdown 纯文本首先需要转换为 HTML。 这可以通过 remarkrehype 完成。

remark 是围绕 markdown 的一系列工具。 rehype 是相同的,但是适用于 HTML。例如,以下代码片段将 markdown 转换为 HTML:

import { unified } from 'unified'
import remarkParse from 'remark-parse'
import remarkRehype from 'remark-rehype'
import rehypeSanitize from 'rehype-sanitize'
import rehypeStringify from 'rehype-stringify'

main()

async function main() {
const file = await unified()
.use(remarkParse) // 转换为 markdown AST
.use(remarkRehype) // 转换为 HTML AST
.use(rehypeSanitize) // 对 HTML 输入进行清理
.use(rehypeStringify) // 将 AST 转换为序列化的 HTML
.process('Hello, Next.js!')

console.log(String(file)) // <p>Hello, Next.js!</p>
}

由于上面显示的 @next/mdx 包已经为您处理了 remarkrehype, 因此您不需要直接使用 remarkrehype, 我们在此描述它是为了更深入地了解 @next/mdx 包在底层执行的操作。

使用基于 Rust 的 MDX 编译器(实验性)

Next.js 支持一个用 Rust 编写的新 MDX 编译器。 这个编译器仍然处于实验阶段,不建议用于生产环境。 要使用新编译器,您需要在将其传递给 withMDX 时配置 next.config.js

next.config.js
module.exports = withMDX({
experimental: {
mdxRs: true,
},
})

有用的网址