用 Nuxt Content 重寫我的 Hugo 部落格
前言
透過 Hugo 建立好部落格後,除了覺得樣式比例有點醜之外,大致上都不錯。
不過用下來總覺得心癢癢,畢竟目前我的技能樹是以 Vue 為主,但部落格目前用的是 Go’s html/template,維護上還是多了些心靈負擔。
畢竟框架的開發體驗相當良好,做什麼都很方便,不論是 UI 框架或是功能,大多都可以找到套件解決。
雖然也研究過要不要使用 Vue 來重寫部落格,但最大的問題是找不到理想的靜態網站生成器(Static Site Generators)。
以 Vue 生態圈來說,VuePress 跟 VitePress 可能是比較好的選擇,但我認為這兩者的結構更適合拿來寫文件,拿來做部落格還是顯得有些怪異。
但正好遇到 Rock 和我分享了 Nuxt Content 這個工具,並且也有寫成文章 - 用 Nuxt Content 重寫我的部落格,對我來說真的是受益良多。
雖然當時我完全沒有接觸過 Nuxt,不過也已經聽聞這個 Vue 的 SSR 框架許久,遷移到 Nuxt Content 也算是多一個主動學習的機會。
不過 Nuxt Content 在我初期評估下來其實 CP 值不高,雖然重新切版因為能寫 Vue,可以預想重建過程會比 Hugo 更輕鬆。但其餘像是輸出速度或頁面載入速度等等,我認為跟 Hugo 相比不會有多少優勢。
但考慮許久之後,我還是決定要遷移到 Nuxt Content。
畢竟我覺得部落格是簡單能展示自己技能的方式之一,相對於用 Hugo 寫的部落格,我相信對於目前的前端領域來說,Nuxt 寫的部落格應該是更加分一點。
還有一點就是 Hugo Theme 程式碼一打開,那模板實在看的眼花撩亂,而且還沒有程式碼 highlight 和 format,真的會讓人失去改 Code 的動力,我想重構或遷移的那天遲早都會到來。
部落格工具選擇
為了讓這次升級有具體的完成方向,我設定了幾個目標:
- 使用並學習 Nuxt3 與 TypeScript
- 提升專案的可維護性
- 新增深色模式
使用 TypeScript 是因為團隊大多專案都開始在使用 TS 開發,我覺得用自己的部落格當作練習是一個不錯的開始。
- 我的部落格需求應該算比較單純,通常遇到的型別問題都很簡單。
- 畢竟是自己的部落格,我肯定不打算濫用 TS 的
as
或是any
得過且過,遇到型別問題會想辦法處理。
而專案的可維護性這點就比較模糊,主要是修改之前寫的 Hugo Theme 真的太可怕了,過一段時間回頭看之前寫的 CSS 也是很頭痛。
改用 Vue 的話在管理元件跟 HTML 上應該會舒適很多,而這次我打算加入語法檢查和 Format On Save 工具 - 使用 ESLint 跟 Prettier。
老實說這兩套我都只在公司的專案中使用,通常前輩都會先處理好環境,倒還沒有自己建構過。
總之也當作是怎麼學習使用,這樣一來程式碼品質應該能有不少提升。
CSS 處理我原本打算使用 Tailwind,最大原因也是因為公司專案正在使用。
所以一來是想要當作練習,藉此更熟悉 Tailwind 的寫法,再來就是我覺得 Tailwind 寫起來真的挺香的,維護 CSS 上真的方便不少,而且省去很多思考 class 命名的困擾。
這部分可以看 Huli 大大寫的這篇 淺談 Atomic CSS 的發展背景與 Tailwind CSS - Huli’s blog,完全能說服我放心的使用 Tailwind。
但就在我要開始建立專案的前幾天,偶然看到 Anthony Fu 這篇 重新构想原子化 CSS 後,我打算直接鬼轉改用 UnoCSS 看看,畢竟有一個效能更好的工具為何不用呢?而且看起來轉換成本也不高,確實值得一試。
因此最後就定案是這樣:
- Framework:Nuxt3 + TypeScript
- UI Framework:UnoCSS
- Extension:ESLint、Prettier
過程遇到的問題
Hugo 的程式碼基本上不太能沿用,所以算是完全從零開始重寫,不過至少可以直接照現有畫面切版,省去很多思考樣式設計的時間。
其實重寫遇到的問題並不多,畢竟是熟悉的語言跟框架,大多遇到的問題幾乎都是自己對 Nuxt3 的不熟悉而造成。
但 Nuxt Content 本身也算是比較新的套件,在開發的過程中加減有遇到一些小問題,例如套件衝突、安裝直接掛掉等等…
不過這些都很快就透過文件說明或參考 github issue 解決。
而下面記錄一些我比較有印象的問題:
Nuxt Content 的路由管理邏輯
比起 Nuxt 本身用 Pages 資料夾底下的 .Vue
幫你自動處理路由,Nuxt Content 比較像是用 Markdown 當作是頁面的路由管理,詳細可以看官方文件說明:Content Directory - Nuxt Content
下面的路由結構是官方範例的一個結構:
而且這個結構對我來說,在檢索上會遇到一些問題,例如我更偏好我的文章路由不是 /frameworks/nuxt
,而是像 /posts/nuxt
,那可能會將資料夾路徑設計成這樣:
但如果我又同時有分類的需求,我要如何知道 nuxt 是要歸類在 frameworks
這個分類目錄下呢?
有個作法是可以使用 front-matter,透過 YAML syntax 的語法來擴充。
並且需要在 nuxt.config.ts
中設定
並且再透過檢索的方式來處理
這樣一來就能找到所有屬於 frameworks
分類的文章。
當然如果需求很複雜,限制搜尋內容的作法還有很多種,例如可以用 Mongo query syntax,又或是把資料都抓出來後,用正則表達式或是關鍵字搭配一些陣列操作解決。
但缺點就是會跟我一樣,花了大量時間在寫檢索文章的邏輯…
Document Driven
Document Driven
是 Nuxt Content 中的一個模式,他新增了 useContent()
,可以更方便的取得 Markdown 的資訊,相當方便。
但我很快的發現了一個問題,使用 Document Driven 後,瀏覽器 console 中總會出現錯誤:
雖然畫面上並沒有產生實際的問題,但仍然有點糟心,好在一番排查後我發現了問題。
在使用到動態路由的路徑中,必須確保每個資料夾路徑都至少有 index.md
能正常渲染內容,讓這個路由不會出現 404。
例如我希望有 /category/life
和 /category/code
這兩個路由,我的 category
結構會像下面這樣。
但這樣是會出現錯誤的,因為如果當我進入 /category
時就會出現問題,所以必須添加 index.md
,最後結構就會如下:
但我希望 /category
的頁面不是文章,又或是想導向 404 頁面,那該怎麼做?
到處求解後我才理解,開啟 Document Driven
之後,Markdown 基本上接近於 Pages 的用途。
比較理想是將 Pages 刪除後改用 Layout 來撰寫頁面結構,然後在 Markdown 中使用 layout-binding 就能解決。
這樣只要在 categoryHome.vue
撰寫自己想顯示的內容即可。
雖然是解決了,但這跟 Nuxt 本身的邏輯有點衝突,對我這個剛學 Nuxt 的新手來說似乎剛好踩錯了坑,實在有點頭疼。
成果
樣式經過調整後,感覺部落格是有比較精緻了一點。
深色模式也一併完成,這部分使用 Nuxt Color Mode 很順利的完成:
其中樣式部分最花時間處理的是程式碼區塊,因為 Nuxt Content 和官方文件顯示的不同,預設是沒有樣式的,只好自行覆蓋撰寫…
旁邊做了一個複製按鈕,如下圖,點擊複製後也會變成勾勾,等用戶滑出範圍後就會再變回複製 icon。
此外,SPA 網頁最大問題應該是初次載入的效能表現不怎麼好,這點在我的部落格上也不例外,即使只有一篇文章也無法跑到綠燈。
可改善主要原因是來自於 TBT
這部分透過壓縮、調整一些資源載入的順序來優化,畢竟部落格網站還算是相當單純的,優化過程相當順利。
輕輕鬆鬆就拉高分數了,但想要 100 分的話可能有點困難,我個人是覺得有綠燈即可。
不過未來這部分應該還需要再優化,畢竟我的部落格圖片放很多,最容易遇到的大概就是圖片載入造成 LCP 問題。
而且目前是用 SSG 輸出,所以無法透過 NuxtImg 優化,如果部落格的最新文章用到比較大的圖片,可能會顯著降低效能。
如果是圖片很多的文章頁面那可能就更糟了…當然未來可以考慮改為 SSR 試試。
總之經過這次重構後,我認為改用 Nuxt3 是很正確的選擇,開發體驗真的舒服不少,SPA 切換也挺香的。
UnoCSS 中用 Tailwind 的寫法也讓 CSS 處理更加容易,擺脫了以往要想 class name 跟粗心就會改 A 壞 B 的問題。
而主角 Nuxt Content 我會覺得像是更加自由一點的 VitePress,雖然其實有不少大大小小的限制或問題,但總是能靠 Nuxt 的方式解決。
因為結構的設計,我覺得用在適合 SPA 結構的網站上會比較舒服,特別就像是 Nuxt 這種文件類型的網站。
不然像我的部落格比較偏向傳統以頁面為單位切換的網站來說,反而會因為要處理資料而四處撞牆。
當然現階段最尷尬的可能還是 Document-driven
,開啟後一些後續結構問題跟 Bug 實在雞肋,而且目前還屬於 experimental 功能。
我想現在的架構總有一天還是得重構一次…總之先用一陣子再來看看怎麼調整吧~