跳转到内容
This is an unmaintained snapshot of the Astro v4 docs. View the latest docs.

组件

Astro 组件是 Astro 项目的基础构建模块。它们是纯 HTML、无需客户端运行时的模板组件。你可以通过文件扩展名 .astro 来识别 Astro 组件。

Astro 组件非常灵活的。通常情况下,Astro 组件会包含一些可在页面中复用的 UI,如 header 或简介。在其他时候,Astro 组件可能包含一个较小的 HTML 片段,像是常见的使 SEO 更好的 <meta> 标签集合。Astro 组件甚至可以包含整个页面布局。

Astro 组件最重要的一点是它们不会在客户端上渲染。它们在构建时或使用 服务器端渲染(SSR) 按需呈现为 HTML。你可以在组件 frontmatter 中编写 JavaScript 代码,所有这些代码都将从发送到用户浏览器的最终页面中删除。结果是一个更快的网站,默认情况下不用任何 JavaScript。

当你的 Astro 组件确实需要客户端交互性时,你可以添加 标准 HTML <script> 标签UI 框架组件

Astro 组件是由两个主要部分所组成的——组件 script组件模板。每个部分分工处理最终呈现出一个既容易使用,又有足够的表现力来实现你的想象的框架。

src/components/EmptyComponent.astro
---
// 组件脚本(JavaScript)
---
<!-- 组件模板(HTML + JS 表达式)-->

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';
// 访问传入的组件参数,如 `<X title="Hello, World"/>`
const { title } = Astro.props;
// 获取外部数据,甚至可以从私有 API 和数据库中获取
const data = await fetch ('SOME_SECRET_API_URL/users').then (r => r.json ());
---
<!-- 你的模板在这! -->

代码围栏的设计是为了保证你在其中编写的 JavaScript 被“围起来”。它不会逃到你的前端应用程序中,或落入你的用户手中。你可以安全地在这里写一些昂贵或敏感的代码(比如调用你的私人数据库),而不用担心它会出现在你的用户的浏览器中。

在组件脚本下面的是组件模板。组件模板决定了你的组件的 HTML 输出。

如果你在这里写普通的 HTML,你的组件将在任何 Astro 页面上呈现它被导入和使用的 HTML。

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

src/components/MyFavoritePokemon.astro
---
// 你的组件脚本在这!
import Banner from '../components/Banner.astro';
import ReactPokemonComponent from '../components/ReactPokemonComponent.jsx';
const myFavoritePokemon = [/* ... */];
const { title } = Astro.props;
---
<!-- 支持 HTML 注释! -->
{/* JS注释语法也是有效的! */}
<Banner />
<h1>你好,世界!</h1>
<!-- 使用组件脚本中的 props 和其他变量: -->
<p>{title}</p>
<!-- 包括其他带有 `client:` 指令的激活组件: -->
<ReactPokemonComponent client:visible />
<!-- 混合 HTML 和 JavaScript 表达式,类似于 JSX: -->
<ul>
{myFavoritePokemon.map ((data) => <li>{data.name}</li>)}
</ul>
<!-- 使用模板指令从多个字符串甚至对象来构建类名! -->
<p class:list={["add", "dynamic", {classNames: true}]} />

组件的设计是为了 可复用可组合。你可以在组件中使用其他组件来构建越来越高级的 UI。例如,Button 组件可用于创建 ButtonGroup 组件:

src/components/ButtonGroup.astro
---
import Button from './Button.astro';
---
<div>
<Button title="按钮 1" />
<Button title="按钮 2" />
<Button title="按钮 3" />
</div>

Astro 组件可以定义和接受参数。然后,这些参数可用于组件模板以呈现 HTML。可以在 frontmatter script 中的 Astro.props 中使用。

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

GreetingHeadline.astro
---
// 使用:<GreetingHeadline greeting="你好" name="朋友" />
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="" name={name} />
<p>希望你有美好的一天!</p>

你也可以使用 TypeScript 中的 interface 来定义 Props 类型。Astro 会自动在你的 frontmatter 中找到 Props interface,并为你的项目提供类型警告/错误。这些 props 也可以在从 Astro.props 解构时给出默认值。

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

当没有提供组件参数时,可以给它默认值来使用。

src/components/GreetingHeadline.astro
---
const { greeting = "你好", name = "宇航员" } = 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 /> <!-- 子项会在这 -->
<Footer />
</div>
src/pages/fred.astro
---
import Wrapper from '../components/Wrapper.astro';
---
<Wrapper title="Fred 的页面">
<h2>关于 Fred 的一切</h2>
<p>这里有一些关于 Fred 的东西。</p>
</Wrapper>

这种模式是 Astro 布局组件 的基础:整个 HTML 内容的页面可以用 <SomeLayoutComponent></SomeLayoutComponent> 标签围起来并发送到组件,以在那里定义的公共页面元素中呈现。

Astro 组件也可以有命名插槽。这允许你仅将具有相应插槽名称的 HTML 元素传递到插槽的位置。

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 />
<slot name="after-header" /> <!-- 带有 `slot="after-header"` 属性的子项在这 -->
<Logo />
<h1>{title}</h1>
<slot /> <!-- 没有 `slot` 或有 `slot="default"` 属性的子项在这 -->
<Footer />
<slot name="after-footer" /> <!-- 带有 `slot="after-footer"` 属性的子项在这 -->
</div>

要将 HTML 内容注入特定插槽,可以在任何子元素上使用 slot 属性来指定插槽的名称。组件的所有其他子元素将被注入到 default(未命名)的 <slot /> 中。

src/pages/fred.astro
---
import Wrapper from '../components/Wrapper.astro';
---
<Wrapper title="弗雷德的页面">
<img src="https://my.photo/fred.jpg" slot="after-header" />
<h2>关于弗雷德的一切</h2>
<p>这里有一些关于弗雷德的资料。</p>
<p slot="after-footer">版权所有 2022</p>
</Wrapper>

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

src/components/CustomTable.astro
---
// 创建一个自定义的表格,为头部和主体内容设置具名的 slot 占位符
---
<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="" 属性以指定 "header""body" 内容以注入多行和多列的 HTML 内容。也可以对单个 HTML 元素进行样式设置:

src/components/StockTable.astro
---
import CustomTable from './CustomTable.astro';
---
<CustomTable>
<Fragment slot="header"> <!-- 传递表头 -->
<tr><th>产品名称</th><th>库存单位</th></tr>
</Fragment>
<Fragment slot="body"> <!-- 传递表格主体 -->
<tr><td>人字拖</td><td>64</td></tr>
<tr><td>靴子</td><td>32</td></tr>
<tr><td>运动鞋</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>当没有子项传入插槽时使用此回退</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 组件功能:

  • 它们不支持 frontmatter、服务器端导入或动态表达式。
  • 任何 <script> 标签都不会被打包,被视为具有 is:inline
  • 它们只能 引用 public/ 文件夹中的资源
阅读更多关于如何在 Astro 项目中使用 UI 框架组件 的内容。
贡献

你有什么想法?

社区