MDX
本页

MDX

此文档仅在使用经典 Remix 编译器时相关。希望使用 MDX 的 Vite 用户应使用MDX Rollup(和 Vite)插件

虽然我们认为数据和显示的强分离非常重要,但我们也理解将两者混合在一起的格式,例如MDX(包含嵌入式 JSX 组件的 Markdown)已成为开发人员流行且强大的创作格式。

与其像本文件演示的那样在构建时编译您的内容,如果您通过类似mdx-bundler 的内容在运行时进行此操作,通常会获得更好的用户体验和开发人员体验。它也更具可定制性和功能性。但是,如果您希望在构建时进行此编译,请继续阅读。

Remix 通过两种方式内置支持在构建时使用 MDX

  • 您可以将 .mdx 文件用作您的路由模块之一
  • 您可以将 .mdx 文件导入到您的路由模块之一(在 app/routes 中)

路由

在 Remix 中开始使用 MDX 的最简单方法是创建一个路由模块。就像 app/routes 目录中的 .tsx.js.jsx 文件一样,.mdx(和 .md)文件将参与基于自动文件系统的路由。

MDX 路由允许您定义元数据和标头,就像它们是基于代码的路由一样

---
meta:
  - title: My First Post
  - name: description
    content: Isn't this awesome?
headers:
  Cache-Control: no-cache
---

# Hello Content!

上面文档中 --- 之间的行称为“前置 matter”。您可以将它们视为文档的元数据,格式为YAML

您可以在 MDX 中通过全局 attributes 变量引用您的前置 matter 字段

---
componentData:
  label: Hello, World!
---

import SomeComponent from "~/components/some-component";

# Hello MDX!

<SomeComponent {...attributes.componentData} />

示例

通过创建一个 app/routes/posts.first-post.mdx,我们可以开始撰写一篇博文

---
meta:
  - title: My First Post
  - name: description
    content: Isn't this just awesome?
---

# Example Markdown Post

You can reference your frontmatter data through "attributes". The title of this post is {attributes.meta.title}!

高级示例

您甚至可以在您的 mdx 文件中导出此模块中的所有其他内容,就像在常规路由模块中一样,例如 loaderactionhandle

---
meta:
  - title: My First Post
  - name: description
    content: Isn't this awesome?

headers:
  Cache-Control: no-cache

handle:
  someData: abc
---

import styles from "./first-post.css";

export const links = () => [
  { rel: "stylesheet", href: styles },
];

import { json } from "@remix-run/node";
import { useLoaderData } from "@remix-run/react";

export const loader = async () => {
  return json({ mamboNumber: 5 });
};

export function ComponentUsingData() {
  const { mamboNumber } = useLoaderData<typeof loader>();
  return <div id="loader">Mambo Number: {mamboNumber}</div>;
}

# This is some markdown!

<ComponentUsingData />

模块

除了路由级别的 MDX 之外,您还可以像常规 JavaScript 模块一样在任何地方自己导入这些文件。

当您导入 .mdx 文件时,模块的导出为

  • default:用于消费的 React 组件
  • attributes:前置 matter 数据作为对象
  • filename:源文件的基名称(例如,“first-post.mdx”)
import Component, {
  attributes,
  filename,
} from "./first-post.mdx";

博客使用示例

以下示例演示了如何使用 MDX 构建简单的博客,包括帖子本身的各个页面和显示所有帖子的索引页面。

import { json } from "@remix-run/node"; // or cloudflare/deno
import { Link, useLoaderData } from "@remix-run/react";

// Import all your posts from the app/routes/posts directory. Since these are
// regular route modules, they will all be available for individual viewing
// at /posts/a, for example.
import * as postA from "./posts/a.mdx";
import * as postB from "./posts/b.md";
import * as postC from "./posts/c.md";

function postFromModule(mod) {
  return {
    slug: mod.filename.replace(/\.mdx?$/, ""),
    ...mod.attributes.meta,
  };
}

export async function loader() {
  // Return metadata about each of the posts for display on the index page.
  // Referencing the posts here instead of in the Index component down below
  // lets us avoid bundling the actual posts themselves in the bundle for the
  // index page.
  return json([
    postFromModule(postA),
    postFromModule(postB),
    postFromModule(postC),
  ]);
}

export default function Index() {
  const posts = useLoaderData<typeof loader>();

  return (
    <ul>
      {posts.map((post) => (
        <li key={post.slug}>
          <Link to={post.slug}>{post.title}</Link>
          {post.description ? (
            <p>{post.description}</p>
          ) : null}
        </li>
      ))}
    </ul>
  );
}

显然,对于包含数千个帖子的博客来说,这不是一个可扩展的解决方案。现实地说,写作很难,所以如果您的博客开始遭受过多的内容困扰,这是一个很棒的问题。如果您获得了 100 篇帖子(恭喜!),我们建议您重新考虑您的策略,并将您的帖子转换为存储在数据库中的数据,以便您不必每次修复错别字时都重新构建和重新部署您的博客。您甚至可以继续使用MDX Bundler 使用 MDX。

高级配置

如果您希望配置自己的 remark 插件,可以通过 remix.config.jsmdx 导出进行配置

const {
  remarkMdxFrontmatter,
} = require("remark-mdx-frontmatter");

// can be an sync / async function or an object
exports.mdx = async (filename) => {
  const [rehypeHighlight, remarkToc] = await Promise.all([
    import("rehype-highlight").then((mod) => mod.default),
    import("remark-toc").then((mod) => mod.default),
  ]);

  return {
    remarkPlugins: [remarkToc],
    rehypePlugins: [rehypeHighlight],
  };
};
文档和示例根据以下许可证获得许可 MIT