React Router v7 已發布。 查看文件
MDX
本頁內容

MDX

此文件僅在使用 傳統 Remix 編譯器 時適用。想要使用 MDX 的 Vite 使用者應該使用 MDX Rollup (和 Vite) 插件

雖然我們認為資料和顯示的強烈分離非常重要,但我們理解混合兩者的格式 (例如 MDX (帶有嵌入式 JSX 元件的 Markdown)) 已成為開發人員中流行且強大的撰寫格式。

與本文件示範的在建置時編譯您的內容不同,如果您透過類似 mdx-bundler 的工具在執行時進行編譯,通常會有更好的使用者體驗和開發者體驗。它也更具自訂性和功能強大。但是,如果您偏好在建置時進行此編譯,請繼續閱讀。

Remix 內建兩種在建置時使用 MDX 的支援方式

  • 您可以使用 .mdx 檔案作為您的路由模組之一
  • 您可以將 .mdx 檔案 import 到您的路由模組 (在 app/routes 中)

路由

在 Remix 中開始使用 MDX 最簡單的方式是建立路由模組。就像您的 app/routes 目錄中的 .tsx.js.jsx 檔案一樣,.mdx (和 .md) 檔案將參與基於檔案系統的自動路由。

MDX 路由允許您像定義基於程式碼的路由一樣定義 meta 和 headers

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

# Hello Content!

上述文件中 --- 之間的行稱為「前言」。您可以將它們視為文件的中繼資料,格式為 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 檔案中匯出此模組中的所有其他內容,就像您在常規路由模組中可以做的那樣,例如 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 模組一樣匯入到任何地方。

當您 import .mdx 檔案時,模組的匯出內容為

  • 預設:用於消費的 React 元件
  • attributes:作為物件的前言資料
  • 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