Markdown 與 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:
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、資料庫或其他地方的內容非常有用。此用途的一個熱門社群套件是 next-mdx-remote
。
須知:請謹慎操作。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: { mdxSource } }
}
import { serialize } from 'next-mdx-remote/serialize'
import { MDXRemote } from 'next-mdx-remote'
export default function RemoteMdxPage({ mdxSource }) {
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: { mdxSource } }
}
導航到 /my-mdx-page-remote
路由應顯示您渲染的 MDX。
佈局
要在 MDX 頁面周圍共享佈局,建立一個佈局元件:
export default function MdxLayout({ children }: { children: React.ReactNode }) {
// 在這裡建立任何共享佈局或樣式
return <div style={{ color: 'blue' }}>{children}</div>
}
export default function MdxLayout({ children }) {
// 在這裡建立任何共享佈局或樣式
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 外掛
您可以選擇性地提供 remark
和 rehype
外掛來轉換 MDX 內容。
例如,您可以使用 remark-gfm
來支援 GitHub Flavored Markdown。
由於 remark
和 rehype
生態系統僅支援 ESM,您需要使用 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
檔案中匯出一個元資料物件:
export const metadata = {
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, { ImageProps } 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 as ImageProps)}
/>
),
...components,
}
}
import Image from 'next/image'
// 此檔案允許您提供自訂 React 元件
// 以在 MDX 檔案中使用。您可以匯入和使用任何
// React 元件,包括內聯樣式、
// 來自其他函式庫的元件等。
export function useMDXComponents(components) {
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。這可以通過 remark
和 rehype
來實現。
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>
}
remark
和 rehype
生態系統包含用於 語法高亮、連結標題、生成目錄 等的外掛。
當使用上述的 @next/mdx
時,您 不需要 直接使用 remark
或 rehype
,因為它已經為您處理了。我們在這裡描述它是為了更深入地了解 @next/mdx
套件在底層做了什麼。
使用基於 Rust 的 MDX 編譯器(實驗性)
Next.js 支援一個用 Rust 編寫的新 MDX 編譯器。此編譯器仍處於實驗階段,不建議用於生產環境。要使用新編譯器,您需要在將 next.config.js
傳遞給 withMDX
時進行配置:
module.exports = withMDX({
experimental: {
mdxRs: true,
},
})