这是用户在 2024-6-8 10:46 为 https://courses.joshwcomeau.com/joy-of-react/project-blog/01.02-mdx-in-next 保存的双语快照页面,由 沉浸式翻译 提供双语支持。了解如何保存?
Skip to content 跳至内容

MDX in Next.js Next.js 中的 MDX

When it comes to using MDX in the context of a Next.js app, there are quite a few options, including:
当谈到在 Next.js 应用程序的上下文中使用 MDX 时,有很多选项,包括:

  • @next/mdx, the official package.
    @next/mdx,官方包。
  • mdx-bundler by Kent C. Dodds.
    mdx-bundler 作者:Kent C. Dodds。
  • next-mdx-remote by Hashicorp.
    Hashicorp 的 next-mdx-remote。

I've experimented with all of these options (and others!), and I've found that the best option for me is next-mdx-remote.
我已经尝试过所有这些选项(以及其他选项!),我发现对我来说最好的选择是 next-mdx-remote

The official @next/mdx package is the simplest option to get started with, but we sacrifice a lot of flexibility. It's a bit too prescriptive for me.
官方的 @next/mdx 包是最简单的入门选项,但我们牺牲了很多灵活性。这对我来说有点太规范了。

Kent's mdx-bundler, on the other hand, feels a little too robust. It includes its own bundler, meaning that our application now has two separate bundlers that have to co-operate. Things like import aliases need to be configured separately; I ran into several issues when I tried to use mdx-bundler on joshwcomeau.com.
另一方面,Kent 的 mdx-bundler 感觉有点过于稳健。它包含自己的捆绑器,这意味着我们的应用程序现在有两个必须合作的独立捆绑器。像导入别名之类的东西需要单独配置;当我尝试在 joshwcomeau.com 上使用 mdx-bundler 时,遇到了几个问题。

Hashicorp's next-mdx-remote is in the Goldilocks zone for me. It's incredibly powerful and flexible. It doesn't include its own bundler, so there's no extra maintenance or compatibility issues. Of the 3 options, I think it has the clearest mental model, in terms of how it works.
Hashicorp 的 next-mdx-remote 对我来说处于金发姑娘区。它非常强大且灵活。它不包含自己的捆绑程序,因此不存在额外的维护或兼容性问题。在这 3 个选项中,我认为就其工作原理而言,它具有最清晰的思维模型。

Also, next-mdx-remote was recently updated to work with React Server Components, and honestly, it got way more user-friendly in the process. next-mdx-remote is easier to use than ever.
此外, next-mdx-remote 最近进行了更新,可以与 React Server 组件一起使用,老实说,它在此过程中变得更加用户友好。 next-mdx-remote 比以往更容易使用。

Now, as always, there are some trade-offs. The biggest downside with next-mdx-remote is that we can't import directly inside our MDX files. But this isn't a huge deal, as you'll soon learn.
现在,一如既往,存在一些权衡。 next-mdx-remote 的最大缺点是我们无法直接导入 MDX 文件。但这并不是什么大不了的事,您很快就会了解到。

Alright, let's learn how to use next-mdx-remote!
好吧,让我们学习如何使用 next-mdx-remote

The big idea 大创意

Here's how it works — we load up our MDX and feed it into an <MDXRemote /> component:
它的工作原理如下——我们加载 MDX 并将其输入到 <MDXRemote /> 组件中:

import { MDXRemote } from 'next-mdx-remote/rsc';
export default function BlogPost() {
const content = ```
# Hello world!
This is the content of an MDX file.
- Yes
- it
- is
```;
return (
<MDXRemote source={content} />
);
}

The <MDXRemote> component will take this raw content string and transform it into a bunch of React elements. In this particular case, it'll be as if we had returned the following JSX:
<MDXRemote> 组件将获取这个原始的 content 字符串并将其转换为一堆 React 元素。在这种特殊情况下,就好像我们返回了以下 JSX:

return (
<>
<h1>Hello world!</h1>
<p>This is the content of an MDX file.</p>
<ul>
<li>Yes</li>
<li>it</li>
<li>is</li>
</ul>
</>
)

next-mdx-remote doesn't care where the MDX comes from. In the example above, it's a hardcoded string, but in a more realistic scenario, we'd load it from somewhere, like a database, a CMS, or the local file system.
next-mdx-remote 不关心 MDX 来自哪里。在上面的示例中,它是一个硬编码字符串,但在更实际的场景中,我们会从某个地方加载它,例如数据库、CMS 或本地文件系统。

For example, using the Node fs (File System) module, we could do something like this:
例如,使用 Node fs (文件系统)模块,我们可以执行如下操作:

import path from 'path';
import fs from 'fs/promises';
import { MDXRemote } from 'next-mdx-remote/rsc';
export default async function BlogPost({ params }) {
// Read the content of a locally-stored file as a string:
const content = await fs.readFile(
path.join(process.cwd(), `/content/${params.slug}.mdx`),
'utf8'
);
return (
<MDXRemote source={content} />
);
}

This is a Server Component, meaning that this code runs exclusively on the server. We look up the .mdx file associated with the provided slug, read all of its content, and pass it into <MDXRemote> to be rendered.
这是一个服务器组件,这意味着该代码仅在服务器上运行。我们查找与提供的 slug 关联的 .mdx 文件,读取其所有内容,并将其传递给 <MDXRemote> 进行渲染。

Custom components 定制组件

The thing that makes MDX so special is the ability to render custom components. We're not limited to the handful of built-in HTML tags!
MDX 的特别之处在于能够渲染自定义组件。我们不仅限于少数内置 HTML 标签!

Consider this code: 考虑这段代码:

import Link from 'next/link';
import { MDXRemote } from 'next-mdx-remote/rsc';
import ContentImage from '@/components/ContentImage';
export default async function BlogPost() {
const content = `
If you run into any problems, you can
[contact us](/contact).
<img
alt="Mailbox clipart"
src="/img/mailbox.svg"
/>
`;
return (
<MDXRemote
source={content}
components={{
a: Link
img: ContentImage,
}}
/>
);
}

If this was a typical Markdown document, it would get compiled into the following HTML:
如果这是一个典型的 Markdown 文档,它将被编译成以下 HTML:

<p>
If you run into any problems, you can <a href="/contact">contact us</a>.
</p>
<img
alt="Mailbox clipart"
src="/img/mailbox.svg"
/>

In MDX, however, we don't compile to HTML, we compile to JSX. And we're able to “remap” Markdown tags to custom React components.
然而,在 MDX 中,我们不编译为 HTML,而是编译为 JSX。我们能够将 Markdown 标签“重新映射”到自定义 React 组件。

And so, here's the JSX that gets compiled:
因此,这是编译后的 JSX:

<p>
If you run into any problems, you can <Link href="/contact">contact us</Link>.
</p>
<ContentImage
alt="Mailbox clipart"
src="/img/mailbox.svg"
/>

Pretty cool right? We can “redefine” any of the built-in HTML tags, to render our own versions instead.
很酷吧?我们可以“重新定义”任何内置 HTML 标签,以呈现我们自己的版本。

Additionally, we can also specify brand-new tags which can be used in our MDX:
此外,我们还可以指定可在 MDX 中使用的全新标签:

import { MDXRemote } from 'next-mdx-remote/rsc'
import FlexDemo from '@/components/FlexDemo';
import SocialShareWidget from '@/components/SocialShareWidget';
export default async function BlogPost() {
const content = `
# Intro to Flexbox
Play around with this demo to get a sense of the Flexbox algorithm:
<FlexDemo />
If you enjoyed this blog post, let your friends know:
<SocialShareWidget />
`;
return (
<MDXRemote
source={content}
components={{
FlexDemo,
SocialShareWidget,
}}
/>
);
}

This is the magic of MDX. We can create custom components like <FlexDemo> and embed them in our MDX documents. We can create as many custom components as we want.
这就是MDX的魔力。我们可以创建像 <FlexDemo> 这样的自定义组件并将它们嵌入到我们的 MDX 文档中。我们可以根据需要创建任意数量的自定义组件。

Drag to outliner or Upload
Close 关闭