MDX
このコンテンツはまだ日本語訳がありません。
MDX
本文档仅在使用 Classic Remix 编译器 时才相关。想要使用 MDX 的 Vite 用户应使用 MDX Rollup(和 Vite)插件。
虽然我们认为数据和显示的严格分离很重要,但我们知道混合两者的格式,例如 MDX(嵌入 JSX 组件的 Markdown)已成为开发人员流行且强大的创作格式。
与本文档所演示的在构建时编译内容不同,如果您在运行时通过 mdx-bundler 之类的工具执行此操作,通常可以获得更好的用户体验和 DX。此外,它还具有更高的可定制性和功能性。但是,如果您更喜欢在构建时执行此编译,请继续阅读。
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!上述文档中 --- 之间的行称为 frontmatter。您可以将它们视为文档的元数据,格式为 YAML。
您可以通过 MDX 中的全局 attributes 变量引用前置字段:
---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 文件中的常规路由模块中使用,例如 loader、action 和 handle:
---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:作为对象的 frontmatter 数据
 - filename:源文件的基本名称(例如 
first-post.mdx) 
import Component, {  attributes,  filename,} from "./first-post.mdx";博客使用示例
下面的示例演示了如何使用 MDX 构建一个简单的博客,其中包括帖子本身的单独页面和显示所有帖子的索引页。
import { json } from "@remix-run/node"; // or cloudflare/denoimport { 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。
高级配置
如果您希望配置自己的备注插件,您可以通过 remix.config.js 的 mdx 导出来进行配置:
const {  remarkMdxFrontmatter,} = require("remark-mdx-frontmatter");
// can be an sync / async function or an objectexports.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],  };};