跳转到内容

组件

Astro 组件是任何 Astro 项目的基本构建块。它们是纯 HTML 模板组件,没有客户端运行时,并使用 .astro 文件扩展名。

Astro 组件非常灵活。一个 Astro 组件可以小到一个 HTML 片段,比如一组使 SEO 更易于处理的通用 <meta> 标签。组件可以是可复用的 UI 元素,比如页眉或个人资料卡。Astro 组件甚至可以包含整个页面布局,或者当位于特殊的 src/pages/ 文件夹中时,它本身就是一个完整的页面。

关于 Astro 组件,最重要的一点是它们不会在客户端上渲染。它们在构建时或按需渲染为 HTML。你可以在组件的 frontmatter 中包含 JavaScript 代码,所有这些代码都将从发送到用户浏览器的最终页面中剥离。结果是网站速度更快,默认情况下不会增加任何 JavaScript 负担。

当你的 Astro 组件确实需要客户端交互时,你可以添加标准的 HTML <script> 标签 UI 框架组件作为“客户端岛屿”。

对于需要渲染个性化或动态内容的组件,你可以通过添加服务器指令来延迟其服务器端渲染。这些“服务器岛屿”将在内容可用时渲染,而不会延迟整个页面的加载。

一个 Astro 组件由两个主要部分组成:**组件脚本**和**组件模板**。每个部分执行不同的工作,但它们共同提供了一个既易于使用又足够强大以处理你可能想要构建的任何东西的框架。

src/components/EmptyComponent.astro
---
// Component Script (JavaScript)
---
<!-- Component Template (HTML + JS Expressions) -->

Astro 使用代码围栏(---)来标识 Astro 组件中的组件脚本。如果你以前写过 Markdown,你可能已经熟悉一个类似的概念,叫做 *frontmatter*。Astro 的组件脚本概念正是受此启发。

你可以使用组件脚本编写渲染模板所需的任何 JavaScript 代码。这可以包括:

  • 导入其他 Astro 组件
  • 导入其他框架组件,如 React
  • 导入数据,如 JSON 文件
  • 从 API 或数据库获取内容
  • 创建你将在模板中引用的变量
src/components/MyComponent.astro
---
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 database
const data = await fetch('SOME_SECRET_API_URL/users').then(r => r.json());
---
<!-- Your template here! -->

代码围栏旨在确保你写入其中的 JavaScript 被“围起来”。它不会泄漏到你的前端应用程序中,也不会落入用户手中。你可以安全地在这里编写高成本或敏感的代码(比如调用你的私有数据库),而不必担心它会最终出现在用户的浏览器中。

组件模板位于代码围栏下方,它决定了你组件的 HTML 输出。

如果你在这里编写纯 HTML,你的组件将在任何导入并使用它的 Astro 页面中渲染该 HTML。

然而,Astro 的组件模板语法还支持 JavaScript 表达式、Astro <style><script> 标签、导入的组件以及特殊的 Astro 指令。在组件脚本中定义的数据和值可以在组件模板中使用,以动态生成 HTML。

src/components/MyFavoritePokemon.astro
---
// 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 组件:

src/components/ButtonGroup.astro
---
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 对象可用。

这是一个接收 greetingname prop 的组件示例。请注意,要接收的 props 是从全局 Astro.props 对象中解构出来的。

src/components/GreetingHeadline.astro
---
// Usage: <GreetingHeadline greeting="Howdy" name="Partner" />
const { greeting, name } = Astro.props;
---
<h2>{greeting}, {name}!</h2>

这个组件在其他 Astro 组件、布局或页面中导入和渲染时,可以作为属性传递这些 props:

src/components/GreetingCard.astro
---
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 解构时赋予默认值。

src/components/GreetingHeadline.astro
---
interface Props {
name: string;
greeting?: string;
}
const { greeting = "Hello", name } = Astro.props;
---
<h2>{greeting}, {name}!</h2>

组件 props 可以设置默认值,以便在没有提供值时使用。

src/components/GreetingHeadline.astro
---
const { greeting = "Hello", name = "Astronaut" } = Astro.props;
---
<h2>{greeting}, {name}!</h2>

<slot /> 元素是外部 HTML 内容的占位符,允许你将子元素从其他文件注入(或“插入”)到你的组件模板中。

默认情况下,传递给组件的所有子元素都将在其 <slot /> 中渲染。

src/components/Wrapper.astro
---
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>
src/pages/fred.astro
---
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 属性来命名:

src/components/Wrapper.astro
---
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 /> 中。

src/pages/fred.astro
---
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>

要将多个 HTML 元素传递到组件的 <slot/> 占位符中而不需要包装 <div>,请在 Astro 的 <Fragment/> 组件上使用 slot="" 属性。

src/components/CustomTable.astro
---
// 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 元素也可以设置样式:

src/components/StockTable.astro
---
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>

请注意,命名插槽必须是组件的直接子元素。你不能通过嵌套元素传递命名插槽。

插槽也可以渲染**后备内容**。当没有匹配的子元素传递给插槽时,<slot /> 元素将渲染其自己的占位符子元素。

src/components/Wrapper.astro
---
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 将传递一个空插槽。当传递空插槽时,后备内容不能用作默认值。只有在找不到插槽元素时,才会显示后备内容。

插槽可以传递给其他组件。例如,在创建嵌套布局时:

src/layouts/BaseLayout.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>
src/layouts/HomeLayout.astro
---
import BaseLayout from './BaseLayout.astro';
---
<BaseLayout>
<slot name="head" slot="head" />
<slot />
</BaseLayout>

现在,传递给 HomeLayout 的默认插槽和 head 插槽将被传递给父组件 BaseLayout

src/pages/index.astro
---
import HomeLayout from '../layouts/HomeLayout.astro';
---
<HomeLayout>
<title slot="head">Astro</title>
<h1>Astro</h1>
</HomeLayout>

Astro 支持将 .html 文件作为组件导入和使用,或者将这些文件放在 src/pages/ 子目录中作为页面。如果你正在重用一个未使用框架构建的现有网站的代码,或者想确保你的组件没有任何动态功能,你可能会想使用 HTML 组件。

HTML 组件必须只包含有效的 HTML,因此缺少 Astro 组件的关键功能:

阅读更多关于在你的 Astro 项目中使用 UI 框架组件的信息。
贡献 社区 赞助