版面
版面是一種 Astro 元件,用來建立可重複使用的 UI 結構,例如頁面模板。
我們習慣以「版面」稱呼在不同頁面共用的 Astro 元件,例如頁首、導覽列、頁尾這種 UI 元素。典型的 Astro 版面元件為 Astro、Markdown 或 MDX 頁面提供:
- 頁面殼層 (
<html>
、<head>
和<body>
標籤) - 供頁面內容嵌入的插槽
<slot />
但其實版面元件沒什麼特別的!它們和其他 Astro 元件一樣,可以接受參數、匯入並使用其他元件,也能包含 UI 框架元件和客戶端腳本。甚至可當作局部 UI 模板,不需要提供整個頁面。
版面元件通常放在專案的 src/layouts
目錄,但這不是強制規定,可以自由選擇要放在哪裡。你甚至可以把它們跟頁面放在一起,只要在版面名稱加上 _
前綴即可。
版面範例
標題為 版面範例---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></html>
---import MySiteLayout from '../layouts/MySiteLayout.astro';---<MySiteLayout title="Home Page"> <p>我的網頁內容被包在一個版面裡!</p></MySiteLayout>
Markdown/MDX 版面
標題為 Markdown/MDX 版面版面對無法自訂頁面格式的 Markdown 和 MDX 頁面來說很實用。
Astro 特殊的 layout
frontmatter 屬性可以指定要把哪一個 .astro
元件當作頁面版面。
---layout: ../layouts/BaseLayout.astrotitle: "Hello, World!"author: "Matthew Phillips"date: "09 Aug 2022"---Astro 版面元件可以透過參數存取所有 frontmatter 屬性。
`layout` 是 Astro 提供的特殊屬性。
`src/pages/` 的 Markdown 和 MDX 檔案都能使用該屬性。
給 Markdown 或 MDX 頁面用的版面通常包含:
frontmatter
參數,能夠存取 Markdown 或 MDX 頁面的 frontmatter 和其他資料。- 預設的
<slot />
,指名頁面的 Markdown/MDX 內容要在哪個位置渲染。
---// 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
or MDXLayoutProps
設定版面的 Props
型別:
---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/MDX 版面能透過 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 }[]
。- (Markdown 專屬)
rawContent()
:取得 Markdown 原始內容的函式,回傳值格式為字串。 - (Markdown 專屬)
compiledContent()
:取得 Markdown 編譯後內容的函式,回傳值格式為 HTML 字串。
舉例來說,Markdown 撰寫的部落格文章可以把下列 Astro.props
物件傳給它的版面:
Astro.props = { file: "/home/user/projects/.../file.md", url: "/zh-tw/guides/markdown-content/", frontmatter: { /** 部落格文章的 frontmatter */ title: "Astro 0.18 Release", date: "Tuesday, July 27 2021", author: "Matthew Phillips", description: "Astro 0.18 is our biggest release since Astro launch.", /** 產生的值 */ file: "/home/user/projects/.../file.md", url: "/zh-tw/guides/markdown-content/" }, headings: [ { "depth": 1, "text": "Astro 0.18 Release", "slug": "astro-018-release" }, { "depth": 2, "text": "Responsive partial hydration", "slug": "responsive-partial-hydration" } /* ... */ ],
/** 只能在 Markdown 使用 */ rawContent: () => "# Astro 0.18 Release\nA little over a month ago, the first public beta [...]", compiledContent: () => "<h1>Astro 0.18 Release</h1>\n<p>A little over a month ago, the first public beta [...]</p>",}
Markdown/MDX 版面也能透過 Astro.props
存取檔案的匯出屬性,不過其中有些差異:
-
標題資訊(即
h1 -> h6
元素)透過headings
陣列存取,而非getHeadings()
函式。 -
file
和url
也可在巢狀frontmatter
屬性下存取(即frontmatter.url
和frontmatter.file
)。 -
無法存取在 frontmatter 以外宣告的數值(例如 MDX 的
export
表達式)。如有類似需求,請考慮匯入版面。
手動匯入版面(MDX)
標題為 手動匯入版面(MDX)需要傳遞資訊到 MDX 版面,但該版面不存在(或無法存在)frontmatter 時,可以匯入 <Layout />
元件,再像其他元件一樣透過參數傳遞給它:
---layout: ../../layouts/BaseLayout.astrotitle: 'My first MDX post'publishDate: '21 September 2022'---import BaseLayout from '../../layouts/BaseLayout.astro';
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 />
的頁面中:
---const { title, fancyJsHelper } = Astro.props;---<!-- --><h1>{title}</h1><slot /> <!-- 你的內容會嵌在這裡 --><p>{fancyJsHelper()}</p><!-- -->
在 .md
、.mdx
、.astro
之間共用相同版面
標題為 在 .md、.mdx、.astro 之間共用相同版面Astro 版面可以接收 .md
、.mdx
檔的 frontmatter
物件,以及從 .astro
檔傳入的任何具名參數。
以下範例中,版面的頁面標題會顯示從 frontmatter YAML title
屬性傳入的值,或從 Astro 元件傳入的 title
屬性:
---const { title } = Astro.props.frontmatter || Astro.props;---<html> <head></head> <body> <h1>{title}</h1> <slot /> </body></html>
巢狀版面
標題為 巢狀版面版面元件不需要包含整頁 HTML 內容。可以將版面拆成更小的元件,並搭配使用版面元件建立更彈性的頁面模板。在不同版面共用程式碼時,這個模式十分實用。
舉例來說,BlogPostLayout.astro
元件可以為部落格文章的標題、日期,以及作者設定樣式。接著,在整個站台共用的 BaseLayout.astro
可以處理剩下的頁面模板,像導覽列、頁尾、SEO meta 標籤、全域樣式、字型等。你也可以從文章接收參數,再傳遞到其他版面,就像跟其他巢狀元件互動一樣。
---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>