组件
Astro 组件是任何 Astro 项目的基本构建块。它们是纯 HTML 模板组件,没有客户端运行时,并使用 .astro
文件扩展名。
如果你了解 HTML,你就已经掌握了编写第一个 Astro 组件所需的全部知识。
Astro 组件非常灵活。一个 Astro 组件可以小到一个 HTML 片段,比如一组使 SEO 更易于处理的通用 <meta>
标签。组件可以是可复用的 UI 元素,比如页眉或个人资料卡。Astro 组件甚至可以包含整个页面布局,或者当位于特殊的 src/pages/
文件夹中时,它本身就是一个完整的页面。
关于 Astro 组件,最重要的一点是它们不会在客户端上渲染。它们在构建时或按需渲染为 HTML。你可以在组件的 frontmatter 中包含 JavaScript 代码,所有这些代码都将从发送到用户浏览器的最终页面中剥离。结果是网站速度更快,默认情况下不会增加任何 JavaScript 负担。
当你的 Astro 组件确实需要客户端交互时,你可以添加标准的 HTML <script>
标签或 UI 框架组件作为“客户端岛屿”。
对于需要渲染个性化或动态内容的组件,你可以通过添加服务器指令来延迟其服务器端渲染。这些“服务器岛屿”将在内容可用时渲染,而不会延迟整个页面的加载。
组件结构
标题为“组件结构”的部分一个 Astro 组件由两个主要部分组成:**组件脚本**和**组件模板**。每个部分执行不同的工作,但它们共同提供了一个既易于使用又足够强大以处理你可能想要构建的任何东西的框架。
---// Component Script (JavaScript)---<!-- Component Template (HTML + JS Expressions) -->
组件脚本
标题为“组件脚本”的部分Astro 使用代码围栏(---
)来标识 Astro 组件中的组件脚本。如果你以前写过 Markdown,你可能已经熟悉一个类似的概念,叫做 *frontmatter*。Astro 的组件脚本概念正是受此启发。
你可以使用组件脚本编写渲染模板所需的任何 JavaScript 代码。这可以包括:
- 导入其他 Astro 组件
- 导入其他框架组件,如 React
- 导入数据,如 JSON 文件
- 从 API 或数据库获取内容
- 创建你将在模板中引用的变量
---import SomeAstroComponent from '../components/SomeAstroComponent.astro';import SomeReactComponent from '../components/SomeReactComponent.jsx';import someData from '../data/pokemon.json';
// Access passed-in component props, like `<X title="Hello, World" />`const { title } = Astro.props;
// Fetch external data, even from a private API or databaseconst data = await fetch('SOME_SECRET_API_URL/users').then(r => r.json());---<!-- Your template here! -->
代码围栏旨在确保你写入其中的 JavaScript 被“围起来”。它不会泄漏到你的前端应用程序中,也不会落入用户手中。你可以安全地在这里编写高成本或敏感的代码(比如调用你的私有数据库),而不必担心它会最终出现在用户的浏览器中。
Astro 组件脚本是 TypeScript,它允许你向 JavaScript 添加额外的语法,以用于编辑器工具和错误检查。
组件模板
标题为“组件模板”的部分组件模板位于代码围栏下方,它决定了你组件的 HTML 输出。
如果你在这里编写纯 HTML,你的组件将在任何导入并使用它的 Astro 页面中渲染该 HTML。
然而,Astro 的组件模板语法还支持 JavaScript 表达式、Astro <style>
和 <script>
标签、导入的组件以及特殊的 Astro 指令。在组件脚本中定义的数据和值可以在组件模板中使用,以动态生成 HTML。
---// Your component script here!import Banner from '../components/Banner.astro';import Avatar from '../components/Avatar.astro';import ReactPokemonComponent from '../components/ReactPokemonComponent.jsx';const myFavoritePokemon = [/* ... */];const { title } = Astro.props;---<!-- HTML comments supported! -->{/* JS comment syntax is also valid! */}
<Banner /><h1>Hello, world!</h1>
<!-- Use props and other variables from the component script: --><p>{title}</p>
<!-- Delay component rendering and provide fallback loading content: --><Avatar server:defer> <svg slot="fallback" class="generic-avatar" transition:name="avatar">...</svg></Avatar>
<!-- Include other UI framework components with a `client:` directive to hydrate: --><ReactPokemonComponent client:visible />
<!-- Mix HTML with JavaScript expressions, similar to JSX: --><ul> {myFavoritePokemon.map((data) => <li>{data.name}</li>)}</ul>
<!-- Use a template directive to build class names from multiple strings or even objects! --><p class:list={["add", "dynamic", { classNames: true }]} />
基于组件的设计
标题为“基于组件的设计”的部分组件被设计为**可复用**和**可组合**的。你可以在其他组件内部使用组件,以构建越来越高级的 UI。例如,一个 Button
组件可以用来创建一个 ButtonGroup
组件:
---import Button from './Button.astro';---<div> <Button title="Button 1" /> <Button title="Button 2" /> <Button title="Button 3" /></div>
组件属性
标题为“组件属性”的部分一个 Astro 组件可以定义和接收 props。然后,这些 props 可以在组件模板中用于渲染 HTML。Props 在你的 frontmatter 脚本中通过全局的 Astro.props
对象可用。
这是一个接收 greeting
和 name
prop 的组件示例。请注意,要接收的 props 是从全局 Astro.props
对象中解构出来的。
---// Usage: <GreetingHeadline greeting="Howdy" name="Partner" />const { greeting, name } = Astro.props;---<h2>{greeting}, {name}!</h2>
这个组件在其他 Astro 组件、布局或页面中导入和渲染时,可以作为属性传递这些 props:
---import GreetingHeadline from './GreetingHeadline.astro';const name = 'Astro';---<h1>Greeting Card</h1><GreetingHeadline greeting="Hi" name={name} /><p>I hope you have a wonderful day!</p>
你也可以使用 TypeScript 的 Props
类型接口来定义你的 props。Astro 会自动识别你 frontmatter 中的 Props
接口,并给出类型警告/错误。这些 props 也可以在从 Astro.props
解构时赋予默认值。
---interface Props { name: string; greeting?: string;}
const { greeting = "Hello", name } = Astro.props;---<h2>{greeting}, {name}!</h2>
组件 props 可以设置默认值,以便在没有提供值时使用。
---const { greeting = "Hello", name = "Astronaut" } = Astro.props;---<h2>{greeting}, {name}!</h2>
<slot />
元素是外部 HTML 内容的占位符,允许你将子元素从其他文件注入(或“插入”)到你的组件模板中。
默认情况下,传递给组件的所有子元素都将在其 <slot />
中渲染。
与 *props*(作为属性传递给 Astro 组件,并可通过 Astro.props
在整个组件中使用)不同,*slots*(插槽)会在其被书写的位置渲染子 HTML 元素。
---import Header from './Header.astro';import Logo from './Logo.astro';import Footer from './Footer.astro';
const { title } = Astro.props;---<div id="content-wrapper"> <Header /> <Logo /> <h1>{title}</h1> <slot /> <!-- children will go here --> <Footer /></div>
---import Wrapper from '../components/Wrapper.astro';---<Wrapper title="Fred's Page"> <h2>All about Fred</h2> <p>Here is some stuff about Fred.</p></Wrapper>
这种模式是 Astro 布局组件的基础:整个页面的 HTML 内容可以被 <SomeLayoutComponent></SomeLayoutComponent>
标签“包裹”起来,并发送给该组件,以在其中定义的通用页面元素内进行渲染。
Astro.slots
工具函数,了解更多访问和渲染插槽内容的方法。
命名插槽
标题为“命名插槽”的部分Astro 组件也可以有命名插槽。这允许你只将具有相应插槽名称的 HTML 元素传递到插槽的位置。
插槽使用 name
属性来命名:
---import Header from './Header.astro';import Logo from './Logo.astro';import Footer from './Footer.astro';
const { title } = Astro.props;---<div id="content-wrapper"> <Header /> <!-- children with the `slot="after-header"` attribute will go here --> <slot name="after-header" /> <Logo /> <h1>{title}</h1> <!-- children without a `slot`, or with `slot="default"` attribute will go here --> <slot /> <Footer /> <!-- children with the `slot="after-footer"` attribute will go here --> <slot name="after-footer" /></div>
要将 HTML 内容注入到特定的插槽中,请在任何子元素上使用 slot
属性来指定插槽的名称。所有其他子元素将被注入到默认的(未命名的)<slot />
中。
---import Wrapper from '../components/Wrapper.astro';---<Wrapper title="Fred's Page"> <img src="https://my.photo/fred.jpg" slot="after-header" /> <h2>All about Fred</h2> <p>Here is some stuff about Fred.</p> <p slot="after-footer">Copyright 2022</p></Wrapper>
在要传递的子元素上使用 slot="my-slot"
属性,以匹配组件中 <slot name="my-slot" />
占位符。
要将多个 HTML 元素传递到组件的 <slot/>
占位符中而不需要包装 <div>
,请在 Astro 的 <Fragment/>
组件上使用 slot=""
属性。
---// Create a custom table with named slot placeholders for header and body content---<table class="bg-white"> <thead class="sticky top-0 bg-white"><slot name="header" /></thead> <tbody class="[&_tr:nth-child(odd)]:bg-gray-100"><slot name="body" /></tbody></table>
使用 slot=""
属性注入多行和多列的 HTML 内容,以指定 "header"
和 "body"
的内容。单个 HTML 元素也可以设置样式:
---import CustomTable from './CustomTable.astro';---<CustomTable> <Fragment slot="header"> <!-- pass table header --> <tr><th>Product name</th><th>Stock units</th></tr> </Fragment>
<Fragment slot="body"> <!-- pass table body --> <tr><td>Flip-flops</td><td>64</td></tr> <tr><td>Boots</td><td>32</td></tr> <tr><td>Sneakers</td><td class="text-red-500">0</td></tr> </Fragment></CustomTable>
请注意,命名插槽必须是组件的直接子元素。你不能通过嵌套元素传递命名插槽。
命名插槽也可以传递给 UI 框架组件!
无法动态生成 Astro 插槽名称,例如在 map 函数中。如果 UI 框架组件需要此功能,最好在该框架内生成这些动态插槽。
插槽的后备内容
标题为“插槽的后备内容”的部分插槽也可以渲染**后备内容**。当没有匹配的子元素传递给插槽时,<slot />
元素将渲染其自己的占位符子元素。
---import Header from './Header.astro';import Logo from './Logo.astro';import Footer from './Footer.astro';
const { title } = Astro.props;---<div id="content-wrapper"> <Header /> <Logo /> <h1>{title}</h1> <slot> <p>This is my fallback content, if there is no child passed into slot</p> </slot> <Footer /></div>
只有在没有匹配的带有 slot="name"
属性的元素传递给命名插槽时,才会显示后备内容。
当插槽元素存在但没有内容可传递时,Astro 将传递一个空插槽。当传递空插槽时,后备内容不能用作默认值。只有在找不到插槽元素时,才会显示后备内容。
传递插槽
标题为“传递插槽”的部分插槽可以传递给其他组件。例如,在创建嵌套布局时:
------<html lang="en"> <head> <meta charset="utf-8" /> <link rel="icon" type="image/svg+xml" href="/favicon.svg" /> <meta name="viewport" content="width=device-width" /> <meta name="generator" content={Astro.generator} /> <slot name="head" /> </head> <body> <slot /> </body></html>
---import BaseLayout from './BaseLayout.astro';---<BaseLayout> <slot name="head" slot="head" /> <slot /></BaseLayout>
命名插槽可以使用 <slot />
标签上的 name
和 slot
属性传递给另一个组件。
现在,传递给 HomeLayout
的默认插槽和 head
插槽将被传递给父组件 BaseLayout
。
---import HomeLayout from '../layouts/HomeLayout.astro';---<HomeLayout> <title slot="head">Astro</title> <h1>Astro</h1></HomeLayout>
HTML 组件
标题为“HTML 组件”的部分Astro 支持将 .html
文件作为组件导入和使用,或者将这些文件放在 src/pages/
子目录中作为页面。如果你正在重用一个未使用框架构建的现有网站的代码,或者想确保你的组件没有任何动态功能,你可能会想使用 HTML 组件。
HTML 组件必须只包含有效的 HTML,因此缺少 Astro 组件的关键功能:
- 它们不支持 frontmatter、服务器端导入或动态表达式。
- 任何
<script>
标签都不会被打包,它们被视为带有is:inline
指令。 - 它们只能引用
public/
文件夹中的资源。
HTML 组件中的 <slot />
元素会像在 Astro 组件中一样工作。如果要改用 HTML Web Component Slot 元素,请向你的 <slot>
元素添加 is:inline
。