在最近的專案優化中,我們嘗試在 VitePress 環境中引入了最前衛的組合:Tailwind CSS v4 與 Nuxt UI v4。雖然這兩者都帶來了極佳的開發體驗,但在 VitePress 這種強依賴伺服器端渲染 (SSR) 場景下,還是踩到了一些有趣的坑。
這篇文記錄了我們如何解決 SSR 水合 (Hydration) 錯誤,以及如何精確映射 Tailwind v4 的色階給 Nuxt UI。
一、 消失的組件:解決 SSR 水合衝突
問題現象
在使用 Nuxt UI 組件(如 <UButton>)後,我們發現生產環境 Build 出來的 HTML 標籤是空的,或者出現組件內容「閃爍」的情況(FOUC)。
原因分析
在 VitePress 的 theme/index.ts 中,我們起初為了避開 SSR 環境下的 window 存取錯誤,將 Nuxt UI 組件註冊放在了 if (!import.meta.env.SSR) 判斷中:
// ❌ 錯誤做法:只在客戶端註冊
if (!import.meta.env.SSR) {
app.use(ui)
app.component('UButton', UButton)
}這會導致 VitePress 在 Node.js 端生成靜態 HTML 時,不認識自定義組件標籤,直接略過它們。
解決方案
應將 組件註冊 與 外掛初始化 分開。組件必須在雙端都註冊,而具有副作用的外掛則保留在客戶端。
// ✅ 正確做法
app.component('UButton', UButton) // 雙端註冊
if (!import.meta.env.SSR) {
app.use(ui) // 僅客戶端
}二、 Tailwind v4 的動態色階映射
問題現象
Nuxt UI v4 大量依賴 CSS 變數進行主題控制。如果只定義了單一的 --ui-primary,組件在 Hover 或 Active 狀態下的顏色過渡會變得非常生硬,因為它找不到對應的 -500、-600 等變數。
優化策略
在 style.css 中利用 Tailwind v4 的 @theme 系統,手動補完所有色階映射。這雖然看起來繁瑣,但能確保組件在所有狀態下都有完美的視覺表現:
:root {
--ui-primary: var(--color-matcha-500);
/* 精確映射 50-950 色階 */
--ui-color-primary-50: var(--color-matcha-50);
--ui-color-primary-100: var(--color-matcha-100);
/* ... 消略中間 ... */
--ui-color-primary-900: var(--color-matcha-900);
--ui-color-primary-950: var(--color-matcha-950);
}三、 強制佈局與 VitePress 的權衡
VitePress 為了閱讀體驗,預設對 .VPDoc 的內容寬度有嚴格限制(通常是 688px)。為了實現「全螢幕且具備專業感」的工程師部落格,我們使用了較強效的 CSS 覆寫。
在使用 !important 覆寫時,必須注意 --vp-content-max-width 的定義,以避免破壞 VitePress 內建的響應式側邊欄邏輯。
總結
| 優化項目 | 核心動作 | 預期效果 |
|---|---|---|
| SSR 水合 | 雙端註冊組件,僅客戶端初始化外掛 | 消除初次渲染的樣式閃爍與標籤缺失 |
| 主題映射 | 手動補完 50-950 CSS 變數 | 確保組件在所有互動狀態下顏色一致 |
| 佈局覆寫 | 調整 CSS 變數與 !important 權重 | 實現全螢幕設計同時保留響應式能力 |
進階挑戰
- 嘗試將 Nuxt UI 的
primary色系動態切換為專案中的其他顏色,並觀察 CSS 變數如何影響組件渲染。 - 研究 VitePress 的
transformHtml鉤子,看是否能更優雅地處理 SSR 期間的自定義標籤。