前面幾天談的是元件和互動,這篇改看內容本身怎麼寫。.md 和 .mdx 長得很像,該怎麼選?是不是一律用能力比較多的那個就好?
Markdown(.md)足以處理幾乎所有內容寫作:文字、標題、清單、程式碼和圖片都能寫,注意力放在內容,不必把文章當成程式。MDX(.mdx)只多一個能力:可以 import 並把元件(包含可互動的 island)直接嵌進內容。預設用 .md;只有某一頁真的需要在內文放入元件,才把那一頁改成 .mdx。這項選擇以檔案為單位,不需要全站統一。
這個實作專案的 blog 目前六篇文章全用 .md。文字、# 標題、清單、用三個反引號框起來的程式碼區塊和圖片,Markdown 全都能處理。寫 .md 時,注意力放在文章內容,不需要把它當成程式來寫。
.md 的另一個好處是可攜性。純 Markdown 幾乎到哪都能 render:貼到 iThome、GitHub 或任何一個 CMS 都認得。內容也不會綁在特定框架的建置流程裡。
大部分內容用 .md 就夠了。
Astro 官方對 MDX 的說明是:
The MDX integration enhances Markdown authoring by enabling the use of JSX variables, expressions, and components.
(MDX 整合強化了 Markdown 的撰寫,讓你能使用 JSX 變數、表達式與元件。)來源:Astro 官方 @astrojs/mdx 文件,查證日 2026-07-21。
在 .mdx 檔裡,可以先在最上面 import 一個元件,再像寫 JSX 一樣,把 <MyComponent /> 直接放進內文中間。如果那是 UI 框架元件(Vue、React),要讓它互動,還要加上 client 指令,遵守 Day 5、Day 7 提過的 opt-in 規則。
這是純 .md 做不到的。.md 只能放文字、Markdown 語法和靜態 HTML,沒辦法 import 元件,也沒辦法讓內文裡的東西變成一座會 hydrate 的 island。
.mdx 嵌入一個 island這個專案安裝了 @astrojs/mdx,並新增 src/pages/demos/mdx-demo.mdx(網址 /demos/mdx-demo)。頁面由一段普通 Markdown 和一個直接嵌進內文的 Vue island 組成:
import ReactionButton from '../../components/ReactionButton.vue';
# 這一頁是用 .mdx 寫的
這段是普通 Markdown:**粗體**、`行內 code`、清單,全都照常。
<ReactionButton client:load slug="mdx-demo" />
執行 build 後,這頁產出的 HTML 帶有一個 island:
<astro-island component-url="/_astro/ReactionButton.DpIq61zS.js" ...>
寫在內文中間的 <ReactionButton client:load /> 會被編譯成一個 island;它會 hydrate,也會把 JS 送到瀏覽器。內容和互動元件放在同一個檔案裡。
安裝 MDX 有兩種方式:
# 二選一
npx astro add mdx
npm install @astrojs/mdx
手動裝的話,記得把 mdx() 加進 astro.config.mjs 的 integrations。
.md 和 .mdx這個專案的 content.config.ts 用以下 glob 載入 blog 內容:
loader: glob({ pattern: '**/[^_]*.{md,mdx}', base: './src/content/blog' })
pattern 裡的 {md,mdx} 會同時收兩種副檔名。安裝 integration 後,把 .mdx 放進 src/content/blog/,它會和現有的 .md 一起載入同一個 collection,使用同一套 schema 驗證與 render 流程。.md 和 .mdx 可以在同一個內容集合並存,不必分成兩區管理。
.md,需要元件才升 .mdx可以照下面兩種情況判斷:
.md。簡單、沒有多一層建置、可攜性最好。.mdx。預設用 .md;只有某一頁需要元件時,才把那一頁改成 .mdx。這項選擇以檔案為單位,不需要全站統一。
MDX 的代價是可攜性。import 和 <Component> 只在 Astro build 裡有效;把 .mdx 貼到 iThome 或只接受純 Markdown 的 CMS,元件語法不會運作,還會直接顯示成文字。需要同時發布到 iThome 和自架 blog 的內容,使用純 Markdown 才能在兩邊正常顯示,因此本系列 30 篇文章都維持 .md。MDX 示範則獨立放在 /demos/mdx-demo,既能展示元件嵌入,也不會讓文章本身依賴 MDX。
內容本身需要嵌入元件,而且不要求可攜性時,才適合用 .mdx;功能較多不是選用它的理由。
.md,需要元件的頁面再改用 .mdx。<ReactionButton /> 不加 client:*,在 MDX 裡只會產生靜態 HTML,不會互動。MDX 仍然遵守 opt-in 規則。{md,mdx},不必為了 MDX 另闢一區。驗收時,確認自己能說清楚 Markdown 和 MDX 各自負責什麼;知道怎麼安裝 MDX,並在 .mdx 裡 import 元件、放入 island;也能判斷頁面只需要純文字(.md),還是需要可互動的元件(.mdx)。另外還要能說明可攜性的取捨:需要同時發布到 iThome 和自架 blog 的文章維持 .md。
有了 .md 和 .mdx,內容一多仍會遇到管理問題:文章手動放在 src/pages,缺少結構和型別,frontmatter 寫錯也無法及時攔下。這時需要一個有 schema、能驗證的內容層。Day 10 會介紹 Content Collections,把散落的檔案整理成有型別的資料。
本日程式碼:step-09|只看這天的改動:step-08...step-09