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 中的預設設定)。

@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

更新專案根目錄的 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)

然後,在 /pages 目錄中建立一個新的 MDX 頁面:

  您的專案
  ├── pages
  │   └── my-mdx-page.mdx
  └── package.json

現在您可以在 MDX 頁面中使用 markdown 並直接匯入 React 元件:

import { MyComponent } from 'my-components'

# 歡迎來到我的 MDX 頁面!

這是一些 **粗體**_斜體_ 文字。

這是一個 markdown 列表:

-
-
-

看看我的 React 元件:

<MyComponent />

導航至 /my-mdx-page 路由應該會顯示您渲染的 MDX。

遠端 MDX

如果您的 markdown 或 MDX 檔案或內容位於 其他地方,您可以在伺服器上動態獲取它。這對於儲存在單獨的本地資料夾、CMS、資料庫或其他任何地方的內容非常有用。

有兩個流行的社群套件用於獲取 MDX 內容:

須知:請謹慎操作。MDX 會編譯為 JavaScript 並在伺服器上執行。您應該只從受信任的來源獲取 MDX 內容,否則可能導致遠端程式碼執行 (RCE)。

以下範例使用 next-mdx-remote

import { serialize } from 'next-mdx-remote/serialize'
import { MDXRemote, MDXRemoteSerializeResult } from 'next-mdx-remote'

interface Props {
  mdxSource: MDXRemoteSerializeResult
}

export default function RemoteMdxPage({ mdxSource }: Props) {
  return <MDXRemote {...mdxSource} />
}

export async function getStaticProps() {
  // MDX 文字 - 可以來自本地檔案、資料庫、CMS、fetch 等任何地方...
  const res = await fetch('https:...')
  const mdxText = await res.text()
  const mdxSource = await serialize(mdxText)
  return { props: { source: mdxSource } }
}

導航至 /my-mdx-page-remote 路由應該會顯示您渲染的 MDX。

佈局

要在 MDX 頁面周圍共享佈局,請建立一個佈局元件:

export default function MdxLayout({ children }: { children: React.ReactNode }) {
  // 在此建立任何共享佈局或樣式
  return <div style={{ color: 'blue' }}>{children}</div>
}

然後,將佈局元件匯入 MDX 頁面,將 MDX 內容包裹在佈局中,並匯出它:

import MdxLayout from '../components/mdx-layout'

# 歡迎來到我的 MDX 頁面!

export default function MDXPage({ children }) {
  return <MdxLayout>{children}</MdxLayout>;

}

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,但有許多解決方案可以為您的 MDX 內容新增 frontmatter,例如:

要使用 @next/mdx 存取頁面元資料,您可以從 .mdx 檔案中匯出一個 meta 物件:

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

# 我的 MDX 頁面

自訂元素

使用 markdown 的一個愉快之處在於它對應到原生 HTML 元素,使撰寫快速且直觀:

這是一個 markdown 列表:

-
-
-

上述內容會生成以下 HTML

<p>這是一個 markdown 列表:</p>

<ul>
  <li>一</li>
  <li>二</li>
  <li>三</li>
</ul>

當您想為網站或應用程式設計自己的元素以獲得自訂外觀時,可以傳入短代碼。這些是您自己的自訂元件,對應到 HTML 元素。

為此,請在應用程式根目錄(pages/src/ 的父資料夾)建立 mdx-components.tsx 檔案並新增自訂元素:

import type { MDXComponents } from 'mdx/types'
import Image from 'next/image'

// 此檔案允許您提供自訂 React 元件
// 以在 MDX 檔案中使用。您可以匯入和使用任何
// 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>
}

remarkrehype 生態系統包含外掛程式,用於 語法突顯連結標題生成目錄 等。

當使用上述的 @next/mdx 時,您 不需要 直接使用 remarkrehype,因為它已經為您處理好了。我們在此描述它是為了更深入地了解 @next/mdx 套件在底層做了什麼。

使用基於 Rust 的 MDX 編譯器(實驗性)

Next.js 支援一個用 Rust 編寫的新 MDX 編譯器。此編譯器仍處於實驗階段,不建議用於生產環境。要使用新編譯器,您需要在將 next.config.js 傳遞給 withMDX 時進行配置:

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

有用的連結

On this page