import ReadMore from '~/components/ReadMore.astro'

布局是特殊的 Astro 组件,可用于创建可重用的页面模板。

我们通常将“布局”用于提供共享页面的常用 UI 元素(如标题、导航栏和页脚)的 Astro 组件。典型的 Astro 布局组件为 Astro、Markdown 或 MDX 页面提供:

  • 一个 页面外壳<html><head><body> 标签)
  • 一个 <slot /> 来指定单个页面内容应该被注入的位置。

但是,布局组件没有什么特别的!它们可以像任何其他 Astro 组件一样接受 props导入和使用其他组件。它们可以包含UI 框架组件客户端脚本。它们甚至不必提供完整的页面外壳,而是可以用作部分 UI 模板。

然而,如果布局组件确实包含页面外壳,那么它的 <html> 元素必须是该组件中所有其他元素的父元素。

布局组件通常放置在项目中的 src/layouts 目录中,但这不是必须的。你可以选择将它们放置在项目中的任何位置。你甚至可以通过在布局组件名称前面加上“_”将布局组件与页面放在同一个文件夹中。

示例布局

astro
// 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="#">主页</a>      <a href="#">文章</a>      <a href="#">联系</a>    </nav>    <h1>{title}</h1>    <article>      <slot /> <!-- 你的内容会被插入到这里 -->    </article>    <Footer />  </body>  <style>    h1 {      font-size: 2rem;    }  </style></html>
astro
---import MySiteLayout from '../layouts/MySiteLayout.astro';---<MySiteLayout title="Home Page">  <p>我的页面内容,被包裹在一个布局中!</p></MySiteLayout>

详细了解插槽

在布局中使用 TypeScript

任何 Astro 布局都可以通过提供 props 类型来引入类型安全和自动补全功能:

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 布局

页面布局对于没有任何页面格式的 Markdown 页面尤为有用。

Astro 提供了一个特殊的 layout frontmatter 属性,用于src/pages/ 目录中使用基于文件的路由的单个 .md 文件来指定使用哪个 .astro 组件作为页面布局。这个组件允许你提供 <head> 内容,例如 meta 标签(例如 <meta charset="utf-8">)和样式,用于 Markdown 页面。默认情况下,指定的组件可以自动访问 Markdown 文件中的数据。

这是一个特殊属性,当使用内容集合来查询和渲染你的内容时,不会被识别。

markdown
---layout: ../layouts/BlogPostLayout.astrotitle: "Hello, World!"author: "Matthew Phillips"date: "09 Aug 2022"---所有的 frontmatter 属性都可以作为 Astro 布局组件的 props。`layout` 属性是 Astro 提供的唯一一个特殊属性。你可以在 `src/pages/` 目录下的 Markdown 文件中使用它。

一个典型的 Markdown 页面布局包括:

  1. 一个 frontmatter prop,用于访问 Markdown 页面的 frontmatter 和其他数据。
  2. 一个默认的 <slot />,用于指定页面的 Markdown 内容应该被渲染的位置。
astro
---// 1. `frontmatter` prop 提供了访问 frontmatter 和其他数据的能力const { frontmatter } = Astro.props;---<html>  <head>    <!-- 添加其他 Head 元素,例如样式和 meta 标签。 -->    <meta name="viewport" content="width=device-width, initial-scale=1">    <meta charset="utf-8">    <title>{frontmatter.title}</title>  </head>  <body>    <!-- 添加其他 UI 组件,例如通用的头部和页脚。 -->    <h1>{frontmatter.title} by {frontmatter.author}</h1>    <!-- 2. 渲染的 HTML 将被传入默认插槽。 -->    <slot />    <p>写于: {frontmatter.date}</p>  </body></html>

你可以使用 MarkdownLayoutProps 帮助程序设置布局的 Props 类型

astro
---import type { MarkdownLayoutProps } from 'astro';type Props = MarkdownLayoutProps<{  // 在这里定义 frontmatter 属性  title: string;  author: string;  date: string;}>;// 现在,`frontmatter`、`url` 和其他 Markdown 布局属性// 可以通过 TypeScript 类型安全地访问const { frontmatter, url } = Astro.props;---<html>  <head>    <meta charset="utf-8">    <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>写于: {frontmatter.date}</p>  </body></html>

Markdown 布局的 Props

一个 Markdown 布局将通过 Astro.props 访问以下信息:

  • file - 此文件的绝对路径(例如 /home/user/projects/.../file.md)。
  • url - 此页面的 URL(例如 /zh-cn/guides/markdown-content)。
  • frontmatter - Markdown 或 MDX 文档中所有的 frontmatter。
    • frontmatter.file - 与顶级 file 属性相同。
    • frontmatter.url - 与顶级 url 属性相同。
  • headings - Markdown 或 MDX 文档中的标题(h1 -> h6)列表及其相关元数据。此列表遵循类型:{ depth: number; slug: string; text: string }[]
  • rawContent() - 返回原始 Markdown 文档的字符串的函数。
  • compiledContent() - 返回 Markdown 文档编译为 HTML 字符串的异步函数。

:::note Markdown 布局将通过 Astro.props 访问其文件的所有 可用属性但有两点关键区别:

  • 标题信息(即 h1 -> h6 元素)可通过 headings 数组访问,而不是 getHeadings() 函数。

  • fileurl 也作为嵌套的 frontmatter 属性(即 frontmatter.urlfrontmatter.file)可用。

:::

手动导入布局(MDX)

你也可以用同样的方式,使用 MDX 文件的 frontmatter 部分中特殊的 Markdown 布局属性,将 frontmatterheadings props 直接传递给指定的布局组件。

要将 Frontmatter 中不存在(或无法存在)的信息传递到 MDX 布局,你可以导入并使用 <Layout /> 组件。它和其他 Astro 组件一样,不会自动接收任何 props。直接传递任何必要的 props:

mdx
---layout: ../../layouts/BaseLayout.astrotitle: 'My first MDX post'publishDate: '21 September 2022'---import BaseLayout from '../../layouts/BaseLayout.astro';export function fancyJsHelper() {  return "尝试使用 YAML 做到这一点!";}<BaseLayout title={frontmatter.title} fancyJsHelper={fancyJsHelper}>  欢迎来到我的新 Astro 博客,正用着 MDX 呢!</BaseLayout>

然后,你可以通过布局中的 Astro.props 访问你的值,而你的 MDX 内容将被注入到你的 <slot /> 组件所在的页面中:

astro
---const { title, fancyJsHelper } = Astro.props;---<html>  <head>    <!-- -->    <meta charset="utf-8">  </head>  <body>    <!-- -->    <h1>{title}</h1>    <slot /> <!-- 你的内容会被插入到这里 -->    <p>{fancyJsHelper()}</p>    <!-- -->  </body></html>

当使用任何布局(通过 frontmatter layout 属性或通过导入布局)时,你必须在布局中包含 <meta charset="utf-8"> 标签,因为 Astro 不再会自动将其添加到你的 MDX 页面中。

了解更多关于 Astro 的 Markdown 和 MDX 支持,请参阅 Markdown/MDX 指南

嵌套布局

布局组件无需包含整个页面的 HTML。你可以将布局分解为更小的组件,然后重用这些组件以创建更灵活、更强大的布局。

例如,BlogPostLayout.astro 布局组件可以对文章的标题、日期和作者进行样式化。然后,BaseLayout.astro 可以处理页面模板的其余部分,例如导航和页脚。你还可以将从文章接收的 props 传递给另一个布局,就像任何其他嵌套组件一样。

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