科技前沿 科普Web 基础教程建站系列

Astro 入门:组件化开发你的第一个页面

📅 2026-07-11

上篇解释了为什么白物集选择 Astro 作为前端框架。今天我们直接动手——用 Astro 组件拼出一个页面,看看 .astro 文件到底怎么工作。

一个 .astro 文件就是"三个区"

Astro 组件文件只有一个 .astro 后缀,内部分三段:组件脚本区(--- 包裹)、HTML 模板区、样式/脚本区。看白物集导航栏的真实代码:

---
// ★ 组件脚本区 — 跑在服务端,浏览器看不见
interface Props {
  title: string;
  items?: { label: string; href: string }[];
}
const { title, items = [] } = Astro.props;
---

<!-- ★ HTML 模板区 — 服务端渲染,最终输出 HTML -->
<nav class="navbar">
  <a href="/">{title}</a>
  <ul>
    {items.map((item) => (
      <li><a href={item.href}>{item.label}</a></li>
    ))}
  </ul>
</nav>

<style>
  /* ★ 组件样式 — 自动 scoped,只对本组件生效 */
  .navbar { ... }
</style>

<script>
  // ★ 客户端脚本 — 浏览器下载并执行
  document.querySelector('.navbar')?.addEventListener(...);
</script>

--- 里面的代码只跑一次(服务端渲染时),不会发到浏览器。这意味着你可以在这里写 fetch、读数据库、导入 npm 包——用户收到的只是最终的 HTML。

Props:组件怎么接收数据

组件通过 Astro.props 拿到外部传入的数据。白物集的文章卡片是个典型例子:

---
// ArticleCard.astro
export interface Props {
  id: number;
  title: string;
  category: string;
  abstract: string;
  date: string;
  tags?: string[];
}
const { id, title, category, abstract, date, tags = [] } = Astro.props;
---

<a href={`/articles/${id}`} class="card">
  <div class="tag">{category}</div>
  <h3>{title}</h3>
  <p>{abstract}</p>
  <span>📅 {date}</span>
  {tags.length > 0 && <span>🏷️ {tags.slice(0, 3).join(' · ')}</span>}
</a>

定义 Props 接口不是为了好看——Astro 会根据接口对传入参数做类型检查。卡片在首页这样使用:

---
import ArticleCard from '../components/ArticleCard.astro';
---
<ArticleCard id={42} title="标题" category="科技前沿" abstract="摘要" date="2026-07-11" />

模板里的 { } 是 Astro 表达式语法,支持 JS 表达式、三元运算符、.map() 渲染列表、.filter() 筛选数据。不能写 if 语句,但三元运算加 && 短路几乎够用了。

组件嵌套与 Slot

Astro 组件可以组合使用。白物集的 BaseLayout.astro 是所有页面的根组件,它内部用 <slot /> 定义内容插入点:

---
// BaseLayout.astro — 引入了 NavBar,预留了 slot
import NavBar from '../components/NavBar.astro';
---
<!doctype html>
<html>
  <head>
    <title>{title} · 白物集</title>
    <!-- SEO meta、OG、canonical… -->
  </head>
  <body>
    <NavBar title={siteConfig.shortName} items={navItems} />
    <main><slot /></main>   <!-- ← 页面内容插在这里 -->
    <footer>…</footer>
  </body>
</html>

首页 index.astro 用 BaseLayout 包裹,传入的内容自动填充到 <slot /> 位置:

---
import BaseLayout from '../layouts/BaseLayout.astro';
---
<BaseLayout title="首页">
  <!-- 这里的内容会进入 BaseLayout 的 <slot /> -->
  <section>
    <h2>最新文章</h2>
    <!-- 文章列表… -->
  </section>
</BaseLayout>

Astro 还支持具名 slot(<slot name="jsonld" />),用于在特定位置注入内容——白物集的文章页就用这个特性在 <head> 中注入 JSON-LD 结构化数据:

<BaseLayout title={article.title} type="article">
  <Fragment slot="jsonld">
    <script type="application/ld+json">
      {"@context": "https://schema.org", "@type": "Article", …}
    </script>
  </Fragment>
</BaseLayout>

样式作用域陷阱

Astro 的 <style> 默认会被自动 scoped——编译时给当前组件的 HTML 元素添加一个哈希属性,CSS 选择器自动补上这个属性选择器。这就是 NavBar.astro 里的 .navbar { } 不会泄漏到页面其他部分的原因。

但是如果你用了 <style is:global>,样式就会全局生效。白物集的 NavBar 利用这个特性实现 logo 配色随主题切换:

<style is:global>
  /* 全局作用 — 因为 .logo-bg 在 SVG 子元素上,scoped 选择器够不到 */
  .logo-bg { fill: url(#navGrad); }
  [data-theme="dark"] .logo-bg { fill: rgba(255,255,255,0.08); }
</style>

避坑经验:SVG 内联路径和子组件嵌套是 Scoped 样式失效的高发区。遇到样式不生效时,先在浏览器检查元素确认哈希属性是否存在,再决定要不要加 is:global

实战:拼一个页面

现在你应该能看懂白物集首页 index.astro 的完整结构了——它引入 BaseLayout,在服务端用 getArticles() 拿到数据,按 AI/Apple/科技频道分组,用 .filter().sort().map() 渲染卡片列表。全部在 --- 块里完成,浏览器收到的只有干净的 HTML。

下一篇我们进入白物集的真实样式系统——用 daisyUI 搭出统一、可维护的 UI 层,看 CSS 框架怎么和 Astro 组件体系配合。

← 为什么需要框架?从白物集看 Astro 的选择 → Apple 频道 Chrome DevTools MCP:浏览器调试交给 AI →
🍎 Apple 深度分析
本文基于 Apple 公开资料及行业分析撰写。观点仅供参考与学习交流。