告別肥大的資料庫,回歸純粹的寫作體驗

當你要建置一個知識付費平台時,最讓人頭痛的問題通常是:「我的文章要存在哪裡?要怎麼排版?」 傳統的做法是搭建一個包含所見即所得編輯器 (如 CKEditor 或 TinyMCE) 的 CMS (內容管理系統),然後把使用者寫好、帶有一大堆 HTML 標籤的「髒字串」塞進關聯式資料庫裡。 這種做法有三個致命缺點:

  1. 排版容易跑版:特別是當你貼上程式碼 (Code Block) 時,常常會亂成一團。
  2. 資料庫過於肥大:幾十萬字的圖文內容會嚴重拖累資料庫查詢速度,且遷移困難。
  3. 沒有版控機制:你很難去追蹤一篇文章過去修改了什麼,不小心刪除就沒了。

這就是為什麼在 Vibe Tutor 中,我們徹底拋棄了傳統 CMS,擁抱了工程師最愛的 Markdown 檔案系統


什麼是 Markdown?

Markdown 是一種輕量級標記式語言,你每天在寫 GitHub 的 README.md 時都在用它。 利用幾個簡單的符號 (如 # 代表標題,** 代表粗體,``` 代表程式碼區塊),你就能寫出結構清晰、乾淨俐落的文件。

更棒的是,Markdown 檔案就是純文字檔。這代表:

  • 它不佔什麼儲存空間。
  • 它可以完美地使用 Git 進行版本控制 (Version Control)。
  • 你可以使用你最習慣的 IDE (如 VS Code, Cursor) 來撰寫課程,享受最強大的補齊與快捷鍵。

Vibe Tutor 的動態渲染魔法

那麼問題來了,我們要怎麼把放在伺服器資料夾裡的 .md 檔案,變成使用者畫面上漂亮的網頁? 這就是 Next.js 搭配 Node.js 生態系展現神威的時候了。

請打開我們的課程核心檔案:src/app/courses/[...slug]/page.tsx。你會看到我們導入了幾個強大的開源套件:

1. fspath (Node.js 內建模組)

首先,我們利用動態路由傳進來的 slug 參數,拼湊出真實的檔案路徑,並用 fs 將它讀進記憶體中。

import fs from "fs";
import path from "path";

// 假設網址是 /courses/vibe-tutor-web/01-intro
// slug 陣列就是 ['vibe-tutor-web', '01-intro']
const dirPath = path.join(process.cwd(), "content", "courses", ...slug);
const filePath = dirPath + ".md";

const fileContents = fs.readFileSync(filePath, "utf8");

這段程式碼會在 Server 端執行,所以它能直接存取伺服器的硬碟檔案系統!

2. gray-matter (前置資料解析)

你有沒有注意到,我們每篇 Markdown 的最上方,都有用 --- 包起來的區塊?

---
title: "第四章:Markdown 動態渲染技術"
---

這被稱為 Front-matter,通常用 YAML 格式撰寫,用來存放這篇文章的「元資料 (Metadata)」,例如標題、作者、發布日期等。 我們利用 gray-matter 套件,可以輕鬆地將文字檔拆分成「資料區」與「內容區」:

import matter from "gray-matter";

const { data, content } = matter(fileContents);
// data.title 就會是 "第四章:Markdown 動態渲染技術"
// content 就是底下的 Markdown 本文

3. remarkremark-html (Markdown 轉 HTML 引擎)

拿到純粹的 Markdown 本文後,最後一步就是把它翻譯成瀏覽器看得懂的 HTML。 我們使用了 Unified 生態系中最著名的 Markdown 處理器:remark

import { remark } from "remark";
import html from "remark-html";

const processedContent = await remark()
  .use(html, { sanitize: false }) 
  .process(content);

const contentHtml = processedContent.toString();

就這樣,contentHtml 變成了一長串標準的 HTML 字串!

4. dangerouslySetInnerHTML@tailwindcss/typography

最後,我們要把它塞進 React 元件裡。在 React 中插入原始 HTML 字串,必須使用 dangerouslySetInnerHTML 這個特殊的屬性。

<article className="prose prose-invert prose-blue max-w-none">
  <div dangerouslySetInnerHTML={{ __html: contentHtml }} />
</article>

這時候你可能會想:這些被轉換出來的 HTML (例如 <h1>, <p>, <code>),因為沒有掛上 Tailwind 的 class,不就變得毫無樣式了嗎? 別擔心!這就是 @tailwindcss/typography 插件的威力! 我們在 className 裡面加上了 prose,這個插件會自動幫底下所有的 HTML 元素套上最適合閱讀的排版樣式 (行距、字體大小、清單縮排)。 而 prose-invert 則是告訴它目前是暗黑模式,字體要反白處理;prose-blue 則是設定所有的超連結跟引號要使用藍色調。


動態目錄生成系統

除了單篇文章的渲染,Vibe Tutor 還具備「自動生成目錄」的神奇功能。 如果你在網址列只輸入 /courses/vibe-tutor-web,系統會發現這是一個資料夾,而不是一個 Markdown 檔案。 這時候,我們的程式會觸發另一段邏輯:

  1. 使用 fs.readdirSync 掃描該資料夾下所有的 .md 檔案。
  2. 將檔案依照字母順序排列 (這也是為什麼我們檔名要用 01-, 02- 開頭)。
  3. 讀取每個檔案的 gray-matter 抓取 title
  4. 在畫面上動態產生帶有章節編號的目錄連結清單。

這意味著,身為站長的你,只要把新的 Markdown 檔案丟進資料夾,目錄就會自動更新,文章就自動上架了! 這就是 Markdown 系統帶來的極致效率。

在下一章,我們將探討這套系統最關鍵的商業邏輯:「Freemium 權限隔離機制」。如何讓前幾章免費吸引人,後面再把內容鎖起來收錢?敬請期待!

解鎖完整教學內容

本章為付費內容。加入專案即可解鎖超過 5000 字的深度解析,包含 10 個以上神級 Prompt 與真實 Source Code 範例!