iT邦幫忙

2026 iThome 鐵人賽

DAY 4
0
Modern Web

用 Astro 打造 Content-first 前端網站:30 天從靜態內容到會員、資料庫與選型(3rd)系列 第 4

每一頁都要 header、footer,Astro 怎麼讓我只寫一次?

  • 分享至 

  • xImage
  •  

Day 3 的結尾留了一個問題:檔案變成網址之後,每一頁都要 header、footer、同一套外框。如果每個 .astro 檔案都複製一遍,改個導覽列連結,就得一起改 30 個檔案。

用一個 layout 就能解決。在 Astro,layout 是普通的 .astro 元件,負責「把別人的內容包起來」。把每頁相同的外框寫在 layout 裡,中間用 <slot /> 留一個洞;每一頁只要提供自己的內容,就會共用這套外框。

如果你寫過 React,可以把 <slot /> 大致對照成 {children},layout 收到的 titledescription 則是 props;Vue 的 <slot> 連名稱都相同。這些都是熟悉的「共用外框」概念,在 Astro 只是換成 layout 與 slot 的寫法。

layout 就是一個會包東西的元件

先看它最小的骨架長怎樣。把每頁共用的東西寫進去,需要「放內容」的地方擺一個 <slot />

---
// src/layouts/BaseLayout.astro
interface Props {
  title: string;
  description?: string;
}
const { title, description } = Astro.props;
---

<html lang="zh-Hant">
  <head>
    <title>{title}</title>
    <meta name="description" content={description} />
  </head>
  <body>
    <header><nav>…每頁共用的導覽列…</nav></header>
    <main>
      <slot />   <!-- 每頁不同的內容,從這裡掉進來 -->
    </main>
    <footer>…每頁共用的頁尾…</footer>
  </body>
</html>

這段使用的仍是一般 .astro 元件語法。interface Props 宣告「我收哪些資料」,Astro.props 取得傳入的值;<slot /> 則標出「呼叫我的人,內容要放在哪」。

這個檔案放在 src/layouts/,不是 src/pages/。Day 3 提過,只有 src/pages 裡的檔案才是路由;放在 src/layouts/ 的 layout 不會產生網址,而是由頁面 import 使用。

資料走 props,內容走 slot

layout 會從外面收到兩種東西,分別走 props 和 slot:

  • 資料走 props。 <BaseLayout title="關於我" description="..."> 這些寫在標籤上的屬性,進到 Astro.props,是給 layout 拿去填 <title><meta> 用的值。
  • 內容走 slot。 包在 <BaseLayout>…</BaseLayout> 這對標籤「中間」的所有東西,會掉進 <slot /> 的位置。

這個實作專案的首頁就是這樣用的(src/pages/index.astro):

---
import BaseLayout from '../layouts/BaseLayout.astro';
---
<BaseLayout title="用 Astro 打造 Content-first 前端網站" description="...">
  <h1>文章列表</h1>
  <ul>…文章一篇篇列出來…</ul>
</BaseLayout>

titledescription 寫在屬性上,會成為資料;中間的 <h1><ul> 是內容,會出現在 layout 的 <slot /> 位置。首頁只提供自身內容,不必再處理 header、footer 或 <head>,因為這些共用外框已集中寫在 layout 裡。

同一個 slot,可以做很重,也可以做很輕

layout 要放多少內容,由頁面需求決定。同一個 <slot /> 機制可以用在功能較多的 layout,也可以用在只留最小外框的 layout。這個 repo 裡的兩個 layout 正好示範這兩種做法:

BaseLayout.astro BenchLayout.astro
行數 約 130 行 29 行
<slot /> 有(1 行) 有(1 行)
收 props title/description/type/image/article 只有 title/description
<head> 裡有什麼 canonical、Open Graph、Twitter card、BlogPosting JSON-LD 幾乎只有 title 和 charset
用途 capstone 正式頁,集中處理整站 SEO 測試頁的最小外框

BaseLayout.astro 有 130 行,其中只有一行 <slot /> 用來「放內容」。剩下的 129 行都是外框,以及 <head> 裡的 SEO 設定:canonical 網址、社群分享用的 Open Graph、供搜尋引擎讀取的 JSON-LD 結構化資料。每篇文章都需要這些設定,但不必各自重寫。集中到 layout 後,30 篇文章共用同一份 SEO 設定,修改也只需動一個檔案。

每頁都需要、又不該重複的內容,就集中到 layout。Astro 官方也把 layout 定位為「用來提供頁面共用結構的元件」;header、footer 與共用樣式等重複元素可以放在其中,以減少重複並維持每頁一致(來源:Astro 官方 Layouts 文件,查證日 2026-07-21)。這個專案集中的是 SEO。日後換網域或補 meta 標籤時,只需修改 layout,不必逐一修改 30 個頁面。

BenchLayout.astro 則是另一種做法:29 行,同樣有一個 <slot />,但不放 SEO,只保留測試頁需要的最小外框。

<slot /> 只標示「內容放這」。layout 的複雜度由頁面需求決定:內容站的正式頁可以使用完整的 BaseLayout,測試頁則用一層薄殼即可。

需要好幾個洞的時候:named slots

前面的範例都只有「一個洞」。有些頁面版型會需要多個內容位置,例如標題在頂部、側欄在右邊、內文在中間。這時可以替 slot 命名:

<!-- layout 裡:開好幾個具名的洞 -->
<header><slot name="title" /></header>
<aside><slot name="aside" /></aside>
<main><slot /></main>   <!-- 沒寫 name 的是預設洞 -->
<!-- 頁面裡:用 slot="名字" 對應塞進去 -->
<MyLayout>
  <h1 slot="title">關於我</h1>
  <nav slot="aside">…側欄…</nav>
  <p>沒標 slot 的內容,掉進預設的那個洞。</p>
</MyLayout>

<slot>沒填就顯示這行</slot> 還可以提供預設值:如果呼叫端沒有放入對應內容,就顯示標籤中間的文字。

這個實作專案目前每頁只需要一個內容區,因此使用單一預設 <slot />。等到 Day 17 組合網站骨架、需要分開安排頭部/側欄/內文時,再使用 named slots。(named slot 的寫法依 Astro 官方 Slots 文件,基準 Astro v7、查證日 2026-07-21;上面是說明用的最小範例。)

幾個容易踩的點

  • 忘了寫 <slot /> 你包在 <Layout>…</Layout> 中間的內容會整段消失,因為沒有洞,內容無處可去。頁面「白掉一塊」先查這個。
  • layout 放錯資料夾。 放進 src/pages/ 後,它會成為一個可存取的網址;layout 應放在 src/layouts/(或 src/components/),再由頁面 import 使用。
  • props 型別對不上,build 會報錯。 layout 使用 .astro 和 TypeScript interface;如果漏傳 title 或傳入錯誤型別,建置階段就會指出,不必等到使用者開啟頁面後才發現。這也是 interface Props 的用途。

今日驗收

驗收時,請確認自己能說清楚三件事:layout 是普通的 .astro 元件;資料走 props,內容走 slot;同一個 <slot /> 可以用在完整的 BaseLayout,也可以用在精簡的 BenchLayout,複雜度由頁面需求決定。

打開任何一個 Astro 專案,看到 src/layouts/ 裡的檔案,你都能指出哪裡是共用外框、<slot /> 在哪、它收哪些 props。

下一步

layout 是元件,頁面也是元件。Day 5 會比較 .astro 元件與 React 元件,說明 .astro 何時連一行 JavaScript 都不用送到瀏覽器。

本日程式碼:step-04|只看這天的改動:step-03...step-04


上一篇
沒有 router 設定檔,Astro 怎麼靠資料夾決定網址?
下一篇
astro 元件跟 React 元件差在哪?為什麼它預設不送 JavaScript?
系列文
用 Astro 打造 Content-first 前端網站:30 天從靜態內容到會員、資料庫與選型(3rd)9
圖片
  熱門推薦
圖片
{{ item.channelVendor }} | {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言