捨棄 create-react-app 之餘還架了個 astro blog 昭告天下:動態路由與 /tag
你的部落格現在有個不錯的門面、幾篇內容豐富的文章、還有一個 /archive 頁列出所有你發布過的內容。你感覺現在還可以新增一個畫面 /tag,這個畫面會列出你所有文章用過的標籤,並且當使用者點擊各個標籤後,他可以看到所有「包含標籤X」的文章。
如果沒有 astro 的 dynamic route 功能時,要完成以上內容,首先你要:
- 整理出所有文章的標籤,然後在
./src/pages
下建立一個tag
資料夾,新增index.astro
並在此元件內渲染所有的標籤 - 接著在
tag
資料夾中,幫每一個標籤都建立一個[標籤名稱].astro
元件,且在每一個[標籤名稱].astro
中列出所有「帶有標籤X」的文章
但手動維護「對應每一個標籤的 astro 元件」實在太麻煩了,就算可以寫一個腳本自動整理出「目前有在使用的文章標籤」,你還是得根據整理完的資料手動建立每一個標籤的畫面元件。更別說還得避免檔案名稱打錯的問題(比如到底是標籤 test
還是 Test
)。
這些問題 astro 都有想到。要解決這個問題,你需要的東西叫 dynamic route (`3´)!
本文
什麼是 dynamic route
延續上方的標籤情境。要解決這個問題,你只要在 ./src/pages/tag
中新增一個檔案 [tag].astro
(注意檔案命名一定要包含 []
),並在這個 [tag].astro
元件的 Component Script 區匯出(export)一個名為 getStaticPaths
的功能即可:
---
const ALL_UNIQUE_TAGS = [...] // 這是一個包含「所有部落格文章」的標籤陣列,稍後會解釋這個變數怎麼來
export async function getStaticPaths() {
return ALL_UNIQUE_TAGS.map((tag) => ({ params: { tag } }));
}
---
注意事項:
getStaticPaths
必定回傳陣列型態的資料,且每一個項目都須包含params
鍵{ params: { tag } }
中的tag
對應檔案名稱[]
中的內容;假設今天我們將./src/pages/tag
中的檔案命名為[blog_tag].astro
,這裡回傳的物件就要改為{ params: { blog_tag } }
只要做到這兩件事情,你就不再需要煩惱到底用過哪些標籤,astro 會幫你把每一個標籤對應的路由畫面都建立好。
實際應用
(以下講解的程式碼內容請參考 src/pages/tag/[tag].astro 與 tools/post.ts)
首先移動到 tools/post.ts
中。先加工老朋友 ALL_SORTED_POSTS
(此變數詳細資訊可參考第 20 天)來取得全站文章用過的所有標籤,並執行 .flat()
讓陣列變為一維的 string[]
:
const ALL_TAGS = ALL_SORTED_POSTS.map((post) => post.frontmatter.tag)
.flat(2)
.sort();
接著再透過 new Set()
即可取得不重複的標籤陣列:
export const ALL_UNIQUE_TAGS = [...new Set(ALL_TAGS)];
回到 ./src/pages/tag
。我們在 Component Script 部位設定好 getStaticPaths()
後,剩下的工作是「把屬於這個標籤的文章都過濾出來」:
const { tag } = Astro.params;
const posts = ALL_SORTED_POSTS.filter((post) =>
post.frontmatter.tag.flat().includes(tag || '')
);
執行 const { tag } = Astro.params;
就是在取得「對應這個 [tag].astro
」的標籤名稱。比如在路由 /tag/TypeScript
時,tag
為 TypeScript、在 /tag/react
時,tag
就是 react。
取得文章資料後,即可在 Component Template 部位將「屬於標籤X的所有文章」渲染出來:
<Html>
<ul class="post-container">
{posts.map((post) => <PostItem post={post} />)}
</ul>
</Html>
透過 astro 處理 dynamic route 就是如此簡單。
處理 /tag 頁
把 ALL_UNIQUE_TAGS
中的項目放到畫面上,再設定 href
讓使用者可以進入各個 [tag].astro
頁面即可:
<Html>
<ul class="tag-container">
{ALL_UNIQUE_TAGS.map((tag) => (
<li>
<a class="tag" href={`/tag/${tag}`}>
{tag}
</a>
</li>
))}
</ul>
</Html>
總結
把 astro 的動態路由就是這麼簡單,甚至有內建的 pagination 功能,基本上就是 getStaticPaths()
的延伸應用。官方範例十分詳細,有需要可直接參考+魔改即可。
最後,感謝你今天的閱讀 (・ω・)