跳到內容

版面

版面是一種 Astro 元件,用來建立可重複使用的 UI 結構,例如頁面模板。

我們習慣以「版面」稱呼在不同頁面共用的 Astro 元件,例如頁首、導覽列、頁尾這種 UI 元素。典型的 Astro 版面元件為 Astro、Markdown 或 MDX 頁面提供:

  • 頁面殼層<html><head><body> 標籤)
  • 供頁面內容嵌入的插槽 <slot />

但其實版面元件沒什麼特別的!它們和其他 Astro 元件一樣,可以接受參數匯入並使用其他元件,也能包含 UI 框架元件客戶端腳本。甚至可當作局部 UI 模板,不需要提供整個頁面。

然而,如果版面元件有包含頁面殼層,它的 <html> 元素必須是元件裡其他元素的父元素。所有 <style><script> 元素必須被 <html> 包住。

版面元件通常放在專案的 src/layouts 目錄,但這不是強制規定,可以自由選擇要放在哪裡。你甚至可以把它們跟頁面放在一起,只要在版面名稱加上 _ 前綴即可。

src/layouts/MySiteLayout.astro
---
import BaseHead from '../components/BaseHead.astro';
import Footer from '../components/Footer.astro';
const { title } = Astro.props;
---
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<BaseHead title={title}/>
</head>
<body>
<nav>
<a href="#">Home</a>
<a href="#">Posts</a>
<a href="#">Contact</a>
</nav>
<h1>{title}</h1>
<article>
<slot /> <!-- 你的內容會嵌在這裡 -->
</article>
<Footer />
</body>
<style>
h1 {
font-size: 2rem;
}
</style>
</html>
src/pages/index.astro
---
import MySiteLayout from '../layouts/MySiteLayout.astro';
---
<MySiteLayout title="Home Page">
<p>我的網頁內容被包在一個版面裡!</p>
</MySiteLayout>
進一步了解插槽

在版面使用 TypeScript

標題為 在版面使用 TypeScript

所有 Astro 版面都可以藉由提供參數的型別來導入型別安全與自動完成:

src/components/MyLayout.astro
---
interface Props {
title: string;
description: string;
publishDate: string;
viewCount: number;
}
const { title, description, publishDate, viewCount } = Astro.props;
---
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="description" content={description}>
<title>{title}</title>
</head>
<body>
<header>
<p>Published on {publishDate}</p>
<p>Viewed by {viewCount} folks</p>
</header>
<main>
<slot />
</main>
</body>
</html>

版面對無法自訂頁面格式的 Markdown 頁面來說很實用。

Astro 特殊的 layout frontmatter 屬性可以指定要把哪一個 .astro 元件當作頁面版面。這個指定的元件預設可以自動從 Markdown 檔案存取資料。

src/pages/page.md
---
layout: ../layouts/BlogPostLayout.astro
title: "Hello, World!"
author: "Matthew Phillips"
date: "09 Aug 2022"
---
Astro 版面元件可以透過參數存取所有 frontmatter 屬性。
`layout` 是 Astro 提供的特殊屬性。
`src/pages/` 的 Markdown 檔案可以使用該屬性。

給 Markdown 頁面用的版面通常包含:

  1. frontmatter 參數,能夠存取 Markdown 頁面的 frontmatter 和其他資料。
  2. 預設的 <slot />,指名頁面的 Markdown 內容要在哪個位置算繪。
src/layouts/BlogPostLayout.astro
---
// 1. 透過 frontmatter 參數存取 frontmatter 和其他資料
const { frontmatter } = Astro.props;
---
<html>
<head>
<!-- 這裡可以放其他 Head 元素,例如樣式和 meta 標籤。 -->
<title>{frontmatter.title}</title>
</head>
<body>
<!-- 這裡可以放其他 UI 元件,例如共用的頁首和頁尾。 -->
<h1>{frontmatter.title} by {frontmatter.author}</h1>
<!-- 2. 算繪後的 HTML 會傳入預設插槽 -->
<slot />
<p>Written on: {frontmatter.date}</p>
</body>
</html>

你可藉由 MarkdownLayoutProps 設定版面的 Props 型別

src/layouts/BlogPostLayout.astro
---
import type { MarkdownLayoutProps } from 'astro';
type Props = MarkdownLayoutProps<{
// 在此定義 frontmatter 參數的型別
title: string;
author: string;
date: string;
}>;
// 現在,`frontmatter`、`url` 與其他 Markdown 版面屬性
// 都能在保證型別安全的情況下存取
const { frontmatter, url } = Astro.props;
---
<html>
<head>
<link rel="canonical" href={new URL(url, Astro.site).pathname}>
<title>{frontmatter.title}</title>
</head>
<body>
<h1>{frontmatter.title} by {frontmatter.author}</h1>
<slot />
<p>Written on: {frontmatter.date}</p>
</body>
</html>

Markdown 版面參數

標題為 Markdown 版面參數

Markdown 版面能透過 Astro.props 存取下列資訊:

  • file:檔案的絕對路徑 (例如 /home/user/projects/.../file.md)。
  • url:頁面網址(例如 /zh-tw/guides/markdown-content)。
  • frontmatter:Markdown 或 MDX 文件中的所有 frontmatter。
    • frontmatter.file:同最上層的 file 屬性。
    • frontmatter.url:同最上層的 url 屬性。
  • headings:Markdown 或 MDX 文件中的標題(h1 -> h6)列表,包含對應的 metadata。其型別為 { depth: number; slug: string; text: string }[]
  • rawContent():取得 Markdown 原始內容的函式,回傳值格式為字串。
  • compiledContent():取得 Markdown 編譯後內容的函式,回傳值格式為 HTML 字串。

手動匯入版面(MDX)

標題為 手動匯入版面(MDX)

你也可以使用 MDX 檔案 frontmatter 中特殊的 Markdown layout 屬性直接傳遞 frontmatterheadings 參數到指定的版面元件。

需要傳遞資訊到 MDX 版面,但該版面不存在(或無法存在)frontmatter 時,可以匯入 <Layout /> 元件。它就像其他 Astro 元件一樣,無法自動接收任何參數。直接將任何必要的參數傳遞給它:

src/pages/posts/first-post.mdx
---
layout: ../../layouts/BaseLayout.astro
title: 'My first MDX post'
publishDate: '21 September 2022'
---
import BaseLayout from '../../layouts/BaseLayout.astro';
export function fancyJsHelper() {
return "Try doing that with YAML!";
}
<BaseLayout title={frontmatter.title} fancyJsHelper={fancyJsHelper}>
Welcome to my new Astro blog, using MDX!
</BaseLayout>

如此一來,版面便能透過 Astro.props 存取數值,而 MDX 內容則會嵌入到包含 <slot /> 的頁面中:

src/layouts/BaseLayout.astro
---
const { title, fancyJsHelper } = Astro.props;
---
<!-- -->
<h1>{title}</h1>
<slot /> <!-- 你的內容會嵌在這裡 -->
<p>{fancyJsHelper()}</p>
<!-- -->
關於 Astro 對 Markdown 和 MDX 的支援,請參考 Markdown 指南

版面元件不需要包含整頁 HTML 內容。可以將版面拆成更小的元件,並搭配使用版面元件建立更彈性的頁面模板。在不同版面共用程式碼時,這個模式十分實用。

舉例來說,BlogPostLayout.astro 元件可以為部落格文章的標題、日期,以及作者設定樣式。接著,在整個站台共用的 BaseLayout.astro 可以處理剩下的頁面模板,像導覽列、頁尾、SEO meta 標籤、全域樣式、字型等。你也可以從文章接收參數,再傳遞到其他版面,就像跟其他巢狀元件互動一樣。

src/layouts/BlogPostLayout.astro
---
import BaseLayout from './BaseLayout.astro';
const { frontmatter } = Astro.props;
---
<BaseLayout url={frontmatter.url}>
<h1>{frontmatter.title}</h1>
<h2>Post author: {frontmatter.author}</h2>
<slot />
</BaseLayout>
貢獻

你有哪些想法?

社群