Astro

完整详细更深入:https://docs.astro.build/en/basics/astro-pages/…

教程地址:https://docs.astro.build/en/getting-started/

完整详细更深入:https://docs.astro.build/en/basics/astro-pages/#supported-page-files

介绍&为什么

Astro是一个用于构建内容驱动型网站(例如博客、营销和电商)的Web 框架。Astro 以其开创性的全新前端架构而闻名,与其他框架相比,它能够降低 JavaScript 的开销和复杂性。如果您需要一个加载速度快且 SEO 表现优异的网站,那么 Astro 就是您的理想之选。

**Astro 是一个一体化的 Web 框架。**它内置了创建网站所需的一切。此外,它还提供数百种不同的集成API 接口,可根据您的具体用例和需求定制项目。

一些亮点,包括:

  • Islands: 一种针对内容驱动网站进行优化的基于组件的 Web 架构。
  • UI-agnostic: 支持 React、Preact、Svelte、Vue、Solid、HTMX、Web 组件等。
  • Server-first: 将昂贵的渲染从访问者的设备上移开。
  • Zero JS, by default: 更少的客户端 JavaScript 来降低您的网站速度。
  • Content collections: 组织、验证并为您的 Markdown 内容提供 TypeScript 类型安全。
  • 可定制: Partytown、MDX 和数百种集成可供选择。

设计原则

以下五个核心设计原则有助于解释我们构建 Astro 的原因、它要解决的问题以及为什么 Astro 可能是您的项目或团队的最佳选择。

Astro 是……

  1. 内容驱动 Astro 旨在展示您的内容。
  2. **服务器优先:**当网站在服务器上呈现 HTML 时,运行速度更快。
  3. **默认速度很快:**在 Astro 中建立一个缓慢的网站是不可能的。
  4. **易于使用:**您不需要成为专家就可以使用 Astro 构建某些东西。
  5. **以开发人员为中心:**您应该拥有成功所需的资源。

内容驱动

**Astro 旨在构建内容丰富的网站。**这包括营销网站、发布网站、文档网站、博客、作品集、落地页、社区网站和电商网站。如果您有内容需要展示,就需要让其快速触达您的读者。

相比之下,大多数现代 Web 框架都是为构建Web 应用程序而设计的。这些框架擅长在浏览器中构建更复杂、更像应用程序的体验:登录式管理仪表盘、收件箱、社交网络、待办事项列表,甚至像FigmaPing这样的原生应用程序。然而,由于这种复杂性,它们在交付内容时可能难以提供出色的性能。

Astro 从最初作为静态网站构建器开始就专注于内容,这使得 Astro 能够合理地扩展为高性能、强大、动态的 Web 应用程序,同时仍然尊重您的内容和受众。Astro 独特的内容聚焦能力使 Astro 能够做出权衡,并提供无与伦比的性能特性,而这些特性对于更注重应用程序的 Web 框架来说,是无法实现的。

服务器优先

**Astro 尽可能地利用服务器渲染而非浏览器的客户端渲染。**这与传统的服务器端框架(例如 PHP、WordPress、Laravel、Ruby on Rails 等)几十年来一直沿用的方法相同。但您无需学习第二门服务器端语言即可解锁 Astro。使用 Astro,一切仍然只需 HTML、CSS 和 JavaScript(或者,如果您愿意,也可以使用 TypeScript)。

这种方法与其他现代 JavaScript Web 框架(例如 Next.js、SvelteKit、Nuxt、Remix 等)形成了鲜明对比。这些框架最初是为整个网站的客户端渲染而构建的,并包含服务器端渲染,主要是为了解决性能问题。这种方法被称为**单页应用 (SPA) ,与 Astro 的****多页应用 (MPA)**方法相对。

SPA 模型固然有其优势,但其代价是额外的复杂性和性能的权衡。这些权衡会损害页面性能——例如可交互时间 (TTI)等关键指标——这对于注重内容的网站来说意义不大,因为首次加载性能至关重要。

Astro 的服务器优先方法允许您仅在必要时(且完全按照需要)启用客户端渲染。您可以选择添加在客户端运行的 UI 框架组件。您可以利用 Astro 的视图过渡路由器来更精细地控制特定页面的过渡和动画。Astro 的服务器优先渲染(无论是预渲染还是按需渲染)提供了高性能的默认设置,您可以对其进行增强和扩展。

默认快速

良好的性能始终至关重要,但对于那些成功依赖于内容展示的网站来说,这一点尤为重要。事实证明,糟糕的性能会降低你的参与度、转化率和收益。

在很多 Web 框架中,很容易出现这样的情况:开发时网站看起来很棒,但部署后加载速度却异常缓慢。JavaScript 往往是罪魁祸首,因为许多手机和性能较弱的设备很少能达到开发人员笔记本电脑的速度。

Astro 的魅力在于它如何将上述两大价值观——内容至上与服务器优先的架构——巧妙地结合起来,并实现其他框架无法实现的功能。其结果是,每个网站都能获得开箱即用的卓越 Web 性能。我们的目标是:使用 Astro,让网站运行缓慢几乎成为不可能。

与使用最流行的 React Web 框架构建的相同网站相比, Astro 网站的加载速度提高了 40%,JavaScript 代码量却减少了 90%。但别轻信我们的话:看看 Astro 的表现,就知道 Ryan Carniato(Solid.js 和 Marko 的创建者)究竟有多惊艳了

便于使用

Astro 的目标是让每一位 Web 开发者都能轻松上手。Astro 的设计理念是让开发者无论技能水平或 Web 开发经验如何,都能轻松上手,并感到熟悉和易用。

Astro .astroUI 语言是 HTML 的超集:任何有效的 HTML 都是 Astro 模板语法!所以,只要你会写 HTML,就能写 Astro 组件!而且,它还融合了一些我们非常喜欢的、借鉴自其他组件语言的特性,例如 JSX 表达式(React)和默认的 CSS 作用域(Svelte 和 Vue)。这种与 HTML 的亲近性也使得它更容易使用渐进式增强和常见的可访问性模式,且不会产生任何开销。

我们确保您能够使用自己熟悉的 UI 组件语言,甚至可以复用已有的组件。Astro 项目中支持 React、Preact、Svelte、Vue、Solid 以及其他语言(包括 Web 组件)来编写 UI 组件。

Astro 的设计理念是比其他 UI 框架和语言更简单。其中一个重要原因是 Astro 的设计目标是在服务器端渲染,而不是在浏览器端。这意味着你无需担心:hooks(React)、过时的闭包(也适用于 React)、refs(Vue)、可观察对象(Svelte)、原子、选择器、reactions 或派生。由于服务器端没有响应式操作,所有这些复杂性都烟消云散了。

我们最喜欢的一句话是:选择复杂性。Astro的设计初衷是尽可能地减少开发者体验中“必需的复杂性”,尤其是在您首次使用时。您只需使用 HTML 和 CSS 即可在 Astro 中构建一个“Hello World”示例网站。然后,当您需要构建更强大的功能时,您可以逐步添加新功能和 API。

以开发者为中心

我们坚信,只有人们热爱使用 Astro,Astro 才算是一个成功的项目。Astro 拥有您使用 Astro 进行开发所需的一切支持。

Astro 投资于开发人员工具,例如从打开终端那一刻起的出色 CLI 体验、用于语法突出显示的官方 VS Code 扩展、TypeScript 和 Intellisense,以及由数百名社区贡献者积极维护并提供 14 种语言版本的文档。

Islands architecture

Astro 帮助开创并推广了一种名为“孤岛架构”的全新前端架构模式**。**孤岛架构的工作原理是将页面的大部分内容渲染为快速的静态 HTML,并在需要交互性或个性化功能(例如图片轮播)时添加较小的 JavaScript“孤岛”。这避免了单体 JavaScript 负载拖慢许多其他现代 JavaScript Web 框架的响应速度。

“Islands” 架构的总体思路看似简单:在服务器上渲染 HTML 页面,并在高度动态的区域周围注入占位符或插槽 […] 然后,这些占位符或插槽可以在客户端“水合”成小型自包含的小部件,并重用其服务器渲染的初始 HTML。——Jason Miller,Preact 创始人

相比之下,大多数基于 JavaScript 的 Web 框架会将整个网站整合并渲染为一个大型 JavaScript 应用程序(也称为单页应用程序,简称 SPA)。SPA 简洁且功能强大,但由于客户端 JavaScript 的使用量较大,因此存在页面加载性能问题。

SPA 确实有其用武之地,甚至可以嵌入到 Astro 页面中。但是,SPA 缺乏选择性和策略性地进行数据融合的原生能力,这使得它们成为当今大多数 Web 项目的一种不合适的选择。

什么是Islands

在 Astro 中,Islands 是静态 HTML 页面上的增强 UI 组件。

client island 是一个交互式 JavaScript UI 组件,它与页面的其余部分分开,而 server island是一个 UI 组件,它通过服务器渲染其动态内容,与页面的其余部分分开。

两个islands 都以每个组件为基础独立运行昂贵或较慢的进程,以优化页面加载。

Island components

Astro 组件是页面模板的构建块。它们渲染为静态 HTML,无需客户端运行时。

可以将 **client island **想象成一个交互式小部件,漂浮在一片静态、轻量级、服务器渲染的 HTML 海洋中。您可以添加 Server islands 来添加个性化或动态的服务器渲染元素,例如登录访客的个人资料图片。

image-20251010143821551

一个 Island 始终独立于页面上的其他 Island 运行,并且一个页面上可以存在多个 Island。即使客户端 Island 运行在不同的组件上下文中,它们仍然可以共享状态并相互通信。

这种灵活性使 Astro 能够支持多种 UI 框架,例如ReactPreactSvelteVueSolidJS。由于它们是独立的,您甚至可以在每个页面上混合使用多个框架。

备注

虽然大多数开发者只会使用一个 UI 框架,但 Astro 支持在同一个项目中使用多个框架。这让你可以:

  • 选择最适合每个组件的框架。
  • 无需启动新项目即可学习新框架。
  • 即使在不同的框架中工作也能与他人合作。
  • 将现有站点逐步转换为另一个框架,无需停机。

Client Islands

默认情况下,Astro 会自动将每个 UI 组件渲染为 HTML 和 CSS,并自动删除所有客户端 JavaScript。

src/pages/index.astro

vue
<MyReactComponent />

这听起来可能很严格,但这种行为默认可以让 Astro 网站保持快速运行,并防止开发人员意外发送不必要的或不需要的 JavaScript,从而降低网站速度。

只需一条指令,即可将任何静态 UI 组件转变为交互式“孤岛”(interactive island) client:*。Astro 会自动构建并打包您的客户端 JavaScript,以优化性能。

src/pages/index.astro

vue
<!-- This component is now interactive on the page!
     The rest of your website remains static. -->
<MyReactComponent client:load />

使用岛屿功能时,客户端 JavaScript 只会为那些您使用 client:* 指令明确标记的交互式组件加载。

client:*使用岛屿时,客户端 JavaScript 仅为您使用指令标记的显式交互组件加载。

由于交互是在组件级别配置的,您可以根据每个组件的用途为其设置不同的加载优先级。例如,client:idle告诉组件在浏览器空闲时加载,或者client:visible告诉组件仅在进入视口时加载。

client islands好处

使用 Astro Islands 构建网站最明显的优势在于性能:网站的大部分内容都会转换为快速的静态 HTML,并且 JavaScript 只会为需要加载的单个组件加载。JavaScript 是按字节加载速度最慢的资源之一,因此每个字节都至关重要。

另一个好处是并行加载。在上面的示例中,低优先级的“图片轮播”岛不需要阻塞高优先级的“页眉”岛。两者并行加载,并相互独立,这意味着页眉可以立即进行交互,而无需等待页面下方更重的轮播。

更棒的是,您可以精确地告诉 Astro 如何以及何时渲染每个组件。如果图片轮播的加载成本非常高,您可以附加一个特殊的客户端指令,告诉 Astro 仅在轮播在页面上可见时加载它。如果用户从未看到它,它就永远不会加载。

在 Astro 中,开发者需要明确告知 Astro 页面上哪些组件也需要在浏览器中运行。Astro 只会加载页面上所需的组件,网站的其余部分则保留为静态 HTML。

客户端岛屿是 Astro 默认快速性能故事的秘密!

阅读有关在项目中使用 JavaScript 框架组件的更多信息。

Server islands

Server islands是一种将昂贵或缓慢的服务器端代码移出主渲染过程的方法,从而可以轻松地结合高性能静态 HTML 和动态服务器生成的组件。

server:defer指令添加到页面上的任何 Astro 组件,将其变成Server islands:

src/pages/index.astro

vue
---

import Avatar from "../components/Avatar.astro";

---

<Avatar server:defer />

这会将您的页面分解为服务器渲染内容的较小区域,每个区域都可以并行加载。

您的页面主要内容可以立即通过占位符内容(例如通用头像)进行渲染,直到您的Server islands自身内容可用为止。使用Server islands时,包含个性化内容的小组件不会延迟原本静态的页面的渲染(加载快啊)。

此渲染模式旨在实现可移植性。它不依赖于任何服务器基础架构,因此可以与任何主机兼容,从 Docker 容器中的 Node.js 服务器到您选择的无服务器提供商。

Server islands的好处

服务器岛的一大优势是能够动态渲染页面中动态程度更高的部分。这使得页面的外壳和主要内容能够被更积极地缓存,从而提供更快的性能。

另一个好处是提供卓越的访客体验。服务器岛经过优化,加载速度极快,通常甚至在浏览器渲染页面之前即可完成。在服务器岛渲染所需的短暂时间内,您可以显示自定义的后备内容,并避免任何布局偏移。

一个受益于 Astro 服务器孤岛的网站示例是电子商务店面。虽然产品页面的主要内容很少更改,但这些页面通常包含一些动态内容:

  • 标题中的用户头像。
  • 该产品的特别优惠和销售。
  • 用户评论。

使用Server islands来管理这些元素,您的访客将立即看到页面最重要的部分——您的产品。通用头像、加载旋转图标和商店公告可以作为备用内容显示,直到个性化部分可用为止。

建立你的第一个 Astro 博客

在本教程中,您将通过构建一个功能齐全的博客(从零开始到全面上线)来学习 Astro 的主要功能!🚀

在此过程中,您将:

  • 设置开发环境
  • 为您的网站创建页面和博客文章
  • 使用 Astro 组件构建
  • 查询和使用本地文件
  • 为您的网站添加互动性
  • 将您的网站部署到网络上

如果你对HTMLMarkdownCSSJavaScript有一定的了解,那么你就可以开始了!只需按照说明操作,你就能完成整个教程。Astro 适合所有人!🧑‍🚀 👩‍🚀 👨‍🚀

启动 Astro 设置向导

创建新 Astro 站点的首选方法是通过我们的create astro安装向导。

  1. 在终端的命令行中,使用您首选的包管理器运行以下命令:

    bash
    npm create astro@latest
  2. 输入y进行安装create-astro

  3. 当提示询问您在哪里创建项目时,请输入文件夹的名称来为您的项目创建一个新目录,例如 ./tutorial

    笔记

    只能在完全空的文件夹中创建新的 Astro 项目,因此请为您的文件夹选择一个尚不存在的名称!

  4. 您将看到一个简短的入门模板列表,供您选择。使用箭头键(上下)导航到最小(空)模板,然后按回车键 (Enter) 提交您的选择。

  5. 当提示询问您是否安装依赖项时,输入y

  6. 当提示询问您是否初始化新的 git 存储库时,输入y

  7. 打开 VS Code。系统将提示你打开一个文件夹。选择你在安装向导中创建的文件夹。

  8. 如果这是您第一次打开 Astro 项目,您应该会看到一条通知,询问您是否要安装推荐的扩展。点击查看推荐的扩展,然后选择Astro 语言支持扩展。这将为您的 Astro 代码提供语法高亮和自动补全功能。

  9. 确保终端可见并且您可以看到命令提示符,例如:

    user@machine:~/tutorial$

在开发模式下运行 Astro

为了在工作时以网站形式预览您的项目文件,您需要 Astro 以开发 (dev) 模式运行。

启动开发服务器

  1. 通过在 VS Code 的终端中输入以下命令来启动 Astro 开发服务器:

    bash
    npm run dev

    现在您应该在终端中看到 Astro 正在开发模式下运行的确认信息。🚀

查看您的网站预览

您的项目文件包含显示 Astro 网站所需的所有代码,但浏览器负责将您的代码显示为网页。

  1. 单击localhost终端窗口中的链接即可查看新 Astro 网站的实时预览!

    (如果端口可用,Astrohttp://localhost:4321默认使用。)4321

image-20251010151304426

image-20251010151517117

第一次编辑

编辑您的主页

  1. 在代码编辑器中,在资源管理器文件窗格中导航src/pages/index.astro并单击它以在可编辑选项卡中打开文件的内容。

    您的文件内容index.astro应如下所示:

    src/pages/index.astro

    html
    ---
    ---
    
    <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} >
        <title>Astro</title>
      </head>
      <body>
        <h1>Astro</h1>
      </body>
    </html>
  2. 编辑页面内容<body>

    在编辑器中输入以更改页面上的标题文本并保存更改。

    src/pages/index.astro

    <body>
      <h1>Astro</h1>
    	变为
      <h1>My Astro Site</h1>
    </body>
  3. 检查浏览器预览,您应该会看到页面内容已更新为新文本。

恭喜!您现在是一名 Astro 开发者了!

本单元的其余部分将帮助您成功进行版本控制并发布一个可以炫耀的网站。

Units 2 Pages

在本单元中,您将:

  • .astro使用语法创建您的第一个 Astro 页面
  • .md使用 Markdown ( ) 文件添加博客文章
  • 使用以下方式设置单个页面的样式<style>
  • 跨页面应用全局样式

在此过程中,您将了解**文件****的两个部分.astro**如何 协同创建页面,以及如何在页面上使用变量和条件渲染。

现在您知道.astro文件负责您网站上的页面,现在是时候创建一个了!

创建新.astro文件

  1. 在代码编辑器的文件窗格中,导航到src/pages/您将看到现有文件的文件夹index.astro
  2. 在同一文件夹中,创建一个名为 的新文件about.astro
  3. 将内容复制或重新输入index.astro到新about.astro文件中。
  4. 在地址栏中将其添加/about到您的网站预览 URL 的末尾,然后检查是否可以在那里看到页面加载。(例如http://localhost:4321/about

现在,您的“关于”页面应该看起来与第一页完全相同,但我们将改变它!

添加导航链接

为了更轻松地预览所有页面,请<h1>在两个页面(index.astroabout.astro)顶部添加 HTML 页面导航链接:

<a href="/">Home</a>
<a href="/about/">About</a>

与许多框架不同,Astro 使用标准 HTML<a>元素在页面之间导航(也称为路由),并进行传统的页面刷新。

撰写你的第一篇 Markdown 博客文章

现在您已经使用.astro文件构建了页面,是时候使用.md文件发布一些博客文章了!

创建您的第一个.md文件

  1. src/pages/posts/ 处创建一个新目录。
  2. 创建空文件post-1.md/posts/目录中。
  3. /posts/post-1加载路由后面(例如http://localhost:4321/posts/post-1
  4. 请将浏览器网页改为 /posts/post-2。(没有创建文件与空文件对比)

一个是空页面(空白),一个显示的是404

image-20251010153931306

编写 Markdown 内容

复制或输入以下代码到post-1.md

markdown
---
title: 'My First Blog Post'
pubDate: 2022-07-01
description: 'This is the first post of my new Astro blog.'
author: 'Astro Learner'
image:
    url: 'https://docs.astro.build/assets/rose.webp'
    alt: 'The Astro logo on a dark background with a pink glow.'
tags: ["astro", "blogging", "learning in public"]
---
# My First Blog Post

Published on: 2022-07-01

Welcome to my _new blog_ about learning Astro! Here, I will share my learning journey as I build a new website.

## What I've accomplished

1. **Installing Astro**: First, I created a new Astro project and set up my online accounts.

2. **Making Pages**: I then learned how to make pages by creating new `.astro` files and placing them in the `src/pages/` folder.

3. **Making Blog Posts**: This is my first blog post! I now have Astro pages and Markdown posts!

## What's next

I will finish the Astro tutorial, and then keep adding more posts. Watch this space for more to come.
  1. 再次检查浏览器预览http://localhost:4321/posts/post-1。现在您应该可以看到此页面的内容了。它可能尚未正确格式化,但不用担心,您将在本教程的后续部分更新它!
  2. 使用浏览器的开发者工具检查此页面。请注意,虽然您尚未输入任何 HTML 元素,但您的 Markdown 已转换为 HTML。您可以看到标题、段落和列表项等元素。

注意

文件顶部(代码栏内)的信息称为 frontmatter。这些数据(包括标签和帖子图片)是Astro 可以使用的关于您帖子的信息。它不会自动显示在页面上,但您将在本教程的后续部分中使用它来增强您的网站。

添加关于您的动态内容

现在您已经拥有一个包含 HTML 内容的多页网站,是时候添加一些动态 HTML 了!

任何 HTML 文件都是有效的 Astro 语言。不过,Astro 的功能远不止普通的 HTML!

定义并使用变量

打开about.astro,它看起来应该是这样的:

html
---
---
<html lang="en">
  <head>
    <meta charset="utf-8" />
    <meta name="viewport" content="width=device-width" />
    <title>Astro</title>
  </head>
  <body>
    <a href="/">Home</a>
    <a href="/about/">About</a>
    <a href="/blog/">Blog</a>
    <h1>About Me</h1>
    <h2>... and my new Astro site!</h2>

    <p>I am working through Astro's introductory tutorial. This is the second page on my website, and it's the first one I built myself!</p>

    <p>This site will update as I complete more of the tutorial, so keep checking back and see how my journey is going!</p>
  </body>
</html>
  1. 在 frontmatter 脚本中的代码围栏之间添加以下 JavaScript 行:

    src/pages/about.astro

    ---
    
    const pageTitle = "About Me";
    
    ---
  2. 用动态变量替换 HTML 中的静态“Astro”标题和“关于我”标题{pageTitle}

    src/pages/about.astro

    <html lang="en">
      <head>
        <meta charset="utf-8" />
        <meta name="viewport" content="width=device-width" />
        <title>Astro</title>
        <title>{pageTitle}</title>
      </head>
      <body>
        <a href="/">Home</a>
        <a href="/about/">About</a>
        <a href="/blog/">Blog</a>
        <h1>{pageTitle}</h1>
        <h2>... and my new Astro site!</h2>
    
        <p>I am working through Astro's introductory tutorial. This is the second page on my website, and it's the first one I built myself!</p>
    
        <p>This site will update as I complete more of the tutorial, so keep checking back and see how my journey is going!</p>
      </body>
    </html>

您无需直接在 HTML 标签中输入文本,只需分别在文件的两个部分中定义并使用变量即可.astro

  1. 使用 JavaScript 或 TypeScript 表达式在 Astro 脚本中定义变量。
  2. 在 Astro 模板中的花括号内使用{ }这些变量来告诉 Astro 您正在使用一些 JavaScript。

在 Astro 中编写 JavaScript 表达式

js
---
const pageTitle = "About Me";

const identity = {
  firstName: "Sarah",
  country: "Canada",
  occupation: "Technical Writer",
  hobbies: ["photography", "birdwatching", "baseball"],
};

const skills = ["HTML", "CSS", "JavaScript", "React", "Astro", "Writing Docs"];
---
html
<p>Here are a few facts about me:</p>
<ul>
  <li>My name is {identity.firstName}.</li>
  <li>I live in {identity.country} and I work as a {identity.occupation}.</li>
  {identity.hobbies.length >= 2 &&
    <li>Two of my hobbies are: {identity.hobbies[0]} and {identity.hobbies[1]}</li>
  }
</ul>
<p>My skills are:</p>
<ul>
  {skills.map((skill) => <li>{skill}</li>)}
</ul>
  1. 编写 Astro 模板非常类似于编写 HTML,但您可以在其中包含 JavaScript 表达式。
  2. Astro 前言脚本仅包含 JavaScript。
  3. 您可以在文件的任一部分使用所有现代 JavaScript逻辑运算符表达式函数.astro。但是,花括号(仅)在 HTML 模板主体中是必需的。

条件渲染

const happy = true;
const finished = false;
const goal = 3;
vue
{happy && <p>I am happy to be learning Astro!</p>}

{finished && <p>I finished this tutorial!</p>}

{goal === 3 ? <p>My goal is to finish in 3 days.</p> : <p>My goal is not 3 days.</p>}

Style About Page

设置CSS

设置单个页面的样式

使用 Astro 自带的<style></style>标签,您可以为页面上的项目添加样式。向这些标签添加属性指令,可以为您提供更多样式设置方式。

html
<html lang="en">
  <head>
    <meta charset="utf-8" />
    <meta name="viewport" content="width=device-width" />
    <title>{pageTitle}</title>
    <style>
      h1 {
        color: purple;
        font-size: 4rem;
      }
    </style>
  </head>

使用第一个 CSS 变量

Astro<style>标签还可以使用指令引用 frontmatter 脚本中的任何变量define:vars={ {...} }。您可以在代码围栏内定义变量,然后在样式标签中将其用作 CSS 变量

  1. skillColor通过将变量添加到 frontmatter 脚本来定义变量src/pages/about.astro,如下所示:
js
---
const skillColor = "crimson";
---

CSS

html
/*关键在这*/
<style define:vars={{skillColor}}>
  h1 {
    color: purple;
    font-size: 4rem;
  }
  .skill {
    /*关键在这*/
    color: var(--skillColor);
    font-weight: bold;
  }
</style>

全局样式

现在您已经有了样式化的“关于”页面,是时候为您网站的其余部分添加一些全局样式了!

添加全局样式表

您已经看到 Astro<style>标签默认具有作用域,这意味着它只影响其自己文件中的元素。

在 Astro 中,有几种方法可以全局global.css定义样式,但在本教程中,您将创建一个文件并将其导入到每个页面中。样式表和<style>标签的组合使您能够控制整个站点的某些样式,并将某些特定样式精确地应用于您想要的位置。

  1. 在该位置创建一个新文件src/styles/global.css(您必须styles先创建一个文件夹。)
  2. 将以下代码复制到新文件中,global.css

src/styles/global.css

css
html {
  background-color: #f1f5f9;
  font-family: sans-serif;
}

body {
  margin: 0 auto;
  width: 100%;
  max-width: 80ch;
  padding: 1rem;
  line-height: 1.5;
}

* {
  box-sizing: border-box;
}

h1 {
  margin: 1rem 0;
  font-size: 2.5rem;
}

在 中about.astro,将以下导入语句添加到您的前置内容中:

src/pages/about.astro

js
---
import '../styles/global.css';
---

Unit 3 - Components

现在您已经拥有了.astro可以.md在您的网站上生成整个页面的文件,是时候使用 Astro 组件制作和重用较小的 HTML 片段了!

在本单元中,您将学习如何创建Astro 组件以重复使用网站中常见元素的代码。

您将构建:

  • 导航组件,显示指向您页面的链接菜单
  • 包含在每个页面底部的页脚组件
  • 页脚中使用的社交媒体组件,链接到个人资料页面
  • 用于在移动设备上切换导航的交互式菜单组件

在此过程中,您将使用 CSS 和 JavaScript 构建响应屏幕尺寸和用户输入的响应式设计。

制作可重复使用的导航组件

创建新src/components/文件夹

为了保存.astro将生成 HTML 但不会成为您网站上的新页面的文件,您需要在项目中创建一个新文件夹:src/components/

创建导航组件

  1. 创建新文件:src/components/Navigation.astro
  2. 复制用于从任意页面顶部导航页面的链接并将其粘贴到新文件中Navigation.astro
---
---
<a href="/">Home</a>
<a href="/about/">About</a>
<a href="/blog/">Blog</a>

如果文件头部分没有任何内容.astro,则无需编写代码围栏。您可以随时在需要时将其添加回来。

导入并使用Navigation.astro

  1. 返回index.astro并导入代码围栏内的新组件:
---
import Navigation from '../components/Navigation.astro';
---

使用:

<Navigation />
取代
<a href="/">Home</a>
<a href="/about/">About</a>
<a href="/blog/">Blog</a>

您的网站包含与以前相同的 HTML。但现在,这三行代码由您的<Navigation />组件提供。

创建社交媒体组件

由于您可能有多个可关联的在线账户,因此您可以创建一个可重复使用的组件并多次显示它。每次显示时,您都需要传递不同的属性 ( props):在线平台和您的用户名。

  1. 在位置创建一个新文件src/components/Social.astro
  2. 将以下代码复制到新文件中Social.astro
js
---
const { platform, username } = Astro.props;
---
<a href={`https://www.${platform}.com/${username}`}>{platform}</a>

使用:

js
---
const platform = "github";
const username = "withastro";
import Social from './Social.astro';
---

<footer>
  <p>Learn more about my projects on <a href={`https://www.${platform}.com/${username}`}>{platform}</a>!</p>
  <Social platform="twitter" username="astrodotbuild" />
  <Social platform="github" username="withastro" />
  <Social platform="youtube" username="astrodotbuild" />
</footer>

将您的第一个脚本发送到浏览器

让我们添加一个按钮来在移动屏幕尺寸上打开和关闭导航菜单,这需要一些客户端交互!

构建菜单组件

创建一个<Menu />组件来打开和关闭您的移动菜单。

  1. Menu.astro创建名为src/components/

  2. 将以下代码复制到您的组件中。它会创建一个按钮,用于切换导航链接在移动设备上的可见性。(global.css稍后您将添加新的 CSS 样式。)

    src/components/Menu.astro

    html
    <button aria-expanded="false" aria-controls="main-menu" class="menu">
      Menu
    </button>
  3. 将此新组件放置在您的组件<Menu />之前。<Navigation /> Header.astro

---
import Menu from './Menu.astro';
import Navigation from './Navigation.astro';
---
<header>
  <nav>
    <Menu />
    <Navigation />
  </nav>
</header>

为您的菜单组件添加以下样式,包括一些响应样式:

:has(.menu[aria-expanded="true"]) .nav-links {
  display: unset;
}
.menu {
  display: none;
}

编写你的第一个脚本标签

您的标题还不**具有交互性,**因为它无法响应用户输入,例如单击菜单来显示或隐藏导航链接。

添加<script>标签可以让客户端 JavaScript“监听”用户事件并做出相应的响应。

  1. 将以下<script>标签添加到index.astro,就在结束</body>标签之前。

src/pages/index.astro

html
  <Footer />
  <script>
    const menu = document.querySelector('.menu');

    menu.addEventListener('click', () => {
      const isExpanded = menu.getAttribute('aria-expanded') === 'true';
      menu.setAttribute('aria-expanded', !isExpanded);
    });
  </script>
</body>

导入.js文件

您无需在每个页面上直接编写 JavaScript,而是可以将标签的内容移动到项目中<script>其自己的文件中。.js

  1. 创建src/scripts/menu.js(您必须创建一个新/scripts/文件夹)并将您的 JavaScript 移动到其中。

src/scripts/menu.js

js
const menu = document.querySelector('.menu');

menu.addEventListener('click', () => {
  const isExpanded = menu.getAttribute('aria-expanded') === 'true';
  menu.setAttribute('aria-expanded', !isExpanded);
});

<script>将标签上的内容替换index.astro为以下文件导入:

html
  <Footer />
  <script>
    import "../scripts/menu.js";
  </script>
</body>

您之前曾使用过一些 JavaScript 来构建网站的部分内容:

  • 动态定义页面标题和标题
  • 通过“关于”页面上的技能列表进行映射
  • 有条件地显示 HTML 元素

这些命令都在构建时执行,为您的网站创建静态 HTML,然后代码被“丢弃”。

标签中的 JavaScript<script>被发送到浏览器,并可根据用户交互(如刷新页面或切换输入)运行。

Unit 4 - Layouts

现在您可以使用组件进行构建,是时候创建一些自定义布局了!

在本单元中,您将构建布局以在您的页面和博客文章之间共享共同的元素和样式。

为此,您将:

  • 创建可重复使用的布局组件
  • 使用以下方式将内容传递到布局<slot />
  • 将数据从 Markdown 前置内容传递到布局
  • 嵌套多个布局

创建您的第一个布局组件

  1. 创建一个新文件src/layouts/BaseLayout.astro
  2. 的全部内容复制index.astro到新文件中BaseLayout.astro

src/layouts/BaseLayout.astro

html
---
import Header from '../components/Header.astro';
import Footer from '../components/Footer.astro';
import '../styles/global.css';
const pageTitle = "Home Page";
---
<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} />
    <title>{pageTitle}</title>
  </head>
  <body>
    <Header />
    <h1>{pageTitle}</h1>
    <Footer />
    <script>
      import "../scripts/menu.js";
    </script>
  </body>
</html>

在页面上使用布局

  1. src/pages/index.astro处的代码替换为以下内容:
---
import BaseLayout from '../layouts/BaseLayout.astro';
const pageTitle = "Home Page";
---
<BaseLayout>
  <h2>My awesome blog subtitle</h2>
</BaseLayout>

在页脚组件上方添加一个<slot />元素src/layouts/BaseLayout.astro,然后检查主页的浏览器预览,注意这次真正发生了什么变化!

src/layouts/BaseLayout.astro

html
 <body>
    <Header />
    <h1>{pageTitle}</h1>
     <!--这里!-->
    <slot />
    <Footer />
  </body>

这允许您将打开和关闭标签之间写入的子内容<slot />注入(或“插入”)到任何文件中

将页面特定的值作为 props 传递

index.astro使用组件属性将页面标题传递给布局组件:

src/pages/index.astro

html
---
import BaseLayout from '../layouts/BaseLayout.astro';
const pageTitle = "Home Page";
---
<!--传入一个属性-->
<BaseLayout pageTitle={pageTitle}>
  <h2>My awesome blog subtitle</h2>
</BaseLayout>

更改布局组件的脚本BaseLayout.astro以通过接收页面标题Astro.props而不是将其定义为常量。

src/layouts/BaseLayout.astro

js
---
import Header from '../components/Header.astro';
import Footer from '../components/Footer.astro';
import '../styles/global.css';
const pageTitle = "Home Page";
const { pageTitle } = Astro.props;
---

创建并将数据传递到自定义博客布局

现在您已经有了页面布局,是时候添加博客文章布局了!

  • 为您的 Markdown 文件创建新的博客文章布局
  • 将 YAML 前置值作为 props 传递给布局组件

为您的博客文章添加布局

当您在文件中包含layoutfrontmatter 属性时.md,所有 frontmatter YAML 值都可供布局文件使用。

  1. 在以下位置创建新文件src/layouts/MarkdownPostLayout.astro
  2. 将以下代码复制到MarkdownPostLayout.astro

src/layouts/MarkdownPostLayout.astro

---
const { frontmatter } = Astro.props;
---
<meta charset="utf-8" />
<h1>{frontmatter.title}</h1>
<p>Written by {frontmatter.author}</p>
<slot />

添加以下 frontmatter 属性post-1.md

src/pages/posts/post-1.md

markdown
---

layout: ../../layouts/MarkdownPostLayout.astro(设置layout)

title: 'My First Blog Post'
pubDate: 2022-07-01
description: 'This is the first post of my new Astro blog.'
author: 'Astro Learner'
image:
    url: 'https://docs.astro.build/assets/rose.webp'
    alt: 'The Astro logo on a dark background with a pink glow.'
tags: ["astro", "blogging", "learning in public"]
---

注意

使用的时候出现问题,设置layout之后,中文变为乱码

image-20251010163847967

这是因为head中缺少meta标签,加回去就行

html
<head>
    <meta charset="utf-8">
</head>
html
---
const {frontmatter} = Astro.props;
---
<meta charset="UTF-8" />
<h1>{frontmatter.title}</h1>
<slot/>

Unit 5 - Astro API

在本单元中,您将使用索引页、标签页和 RSS 提要增强您的博客。

在此过程中,您将学习如何使用:

  • import.meta.glob()访问项目中的文件数据
  • getStaticPaths()一次创建多个页面(路线)
  • Astro RSS 包用于创建 RSS 提要

博客文章存档

现在您已经有了一些博客文章可以链接,是时候配置博客页面来自动创建它们的列表了!

动态显示帖子列表

  1. 添加以下代码以blog.astro返回有关所有 Markdown 文件的信息。import.meta.glob()将返回一个对象数组,每个博客文章一个。

    src/pages/blog.astro

    js
    ---
    import BaseLayout from '../layouts/BaseLayout.astro'
    const allPosts = Object.values(import.meta.glob('./posts/*.md', { eager: true }));
    const pageTitle = "My Astro Learning Blog";
    ---
  2. 要使用帖子标题和 URL 动态生成整个帖子列表,请<li>使用以下 Astro 代码替换各个标签: src/pages/blog.astro

    html
    <ul>
        {allPosts.map((post: any) => <li><a href={post.url}>{post.frontmatter.title}</a></li>)}
    </ul>

    现在,通过映射返回的数组,可以使用 Astro 内置的 TypeScript 支持动态生成您的整个博客文章列表import.meta.glob()

生成标签页

动态页面路由

您可以使用导出函数getStaticPaths().astro文件中动态创建整套页面page。

动态创建页面

  1. 创建一个新文件src/pages/tags/[tag].astro。请注意,文件名 ( [tag].astro) 使用方括号。将以下代码粘贴到文件中:

    src/pages/tags/[tag].astro

    js
    ---
    import BaseLayout from '../../layouts/BaseLayout.astro';
    
    export async function getStaticPaths() {
      return [
        { params: { tag: "astro" } },
        { params: { tag: "successes" } },
        { params: { tag: "community" } },
        { params: { tag: "blogging" } },
        { params: { tag: "setbacks" } },
        { params: { tag: "learning in public" } },
      ];
    }
    
    const { tag } = Astro.params;
    ---
    <BaseLayout pageTitle={tag}>
      <p>Posts tagged with {tag}</p>
    </BaseLayout>

    getStaticPaths函数返回一个页面路由数组,并且这些路由上的所有页面都将使用文件中定义的相同模板。

    1. 如果您已自定义博客文章,请将各个标签值(例如“astro”、“successes”、“community”等)替换为您自己文章中使用的标签。
    2. 确保每篇博客文章至少包含一个标签,以数组的形式写,例如tags: ["blogging"]
    3. 在浏览器预览中访问http://localhost:4321/tags/astro,您应该会看到一个由 动态生成的页面[tag].astro。请检查是否还为每个标签(例如/tags/successes/tags/community、 和/tags/learning%20in%20public等)或每个自定义标签创建了页面。您可能需要先退出并重启开发服务器才能看到这些新页面。

在动态路由中使用 props

  1. 将以下 props 添加到您的getStaticPaths()功能中,以便使您的所有博客文章的数据可用于每个页面路由。

    确保为数组中的每个路由提供新的 props,然后使这些 props 在函数之外可供组件模板使用。

    src/pages/tags/[tag].astro

    js
    ---
    import BaseLayout from '../../layouts/BaseLayout.astro';
    
    export async function getStaticPaths() {
      const allPosts = Object.values(import.meta.glob('../posts/*.md', { eager: true }));
    
      return [
        {params: {tag: "astro"}, props: {posts: allPosts}},
        {params: {tag: "successes"}, props: {posts: allPosts}},
        {params: {tag: "community"}, props: {posts: allPosts}},
        {params: {tag: "blogging"}, props: {posts: allPosts}},
        {params: {tag: "setbacks"}, props: {posts: allPosts}},
        {params: {tag: "learning in public"}, props: {posts: allPosts}}
      ];
    }
    
    const { tag } = Astro.params;
    const { posts } = Astro.props;
    ---

    使用 Astro 的内置 TypeScript 支持过滤您的帖子列表,筛选自身标签的帖子。

    src/pages/tags/[tag].astro

    js
    ---
    const { tag } = Astro.params;
    const { posts } = Astro.props;
    const filteredPosts = posts.filter((post: any) => post.frontmatter.tags?.includes(tag));
    ---

    现在,您可以更新 HTML 模板,以显示包含页面自身标签的每篇博客文章的列表。将以下代码添加到[tag].astro

    src/pages/tags/[tag].astro

    html
    <BaseLayout pageTitle={tag}>
      <p>Posts tagged with {tag}</p>
      <ul>
        {filteredPosts.map((post: any) => <li><a href={post.url}>{post.frontmatter.title}</a></li>)}
      </ul>
    </BaseLayout>

    您甚至可以重构它以使用您的<BlogPost />组件!(不要忘记在[tag].astro顶部导入此组件。)

    src/pages/tags/[tag].astro

    html
    <BaseLayout pageTitle={tag}>
      <p>Posts tagged with {tag}</p>
      <ul>
        {filteredPosts.map((post: any) => <BlogPost url={post.url} title={post.frontmatter.title}/>)}
      </ul>
    </BaseLayout>

从现有标签生成页面

src/pages/tags/[tag].astro

---
import BaseLayout from '../../layouts/BaseLayout.astro';

export async function getStaticPaths() {
  const allPosts = Object.values(import.meta.glob('../posts/*.md', { eager: true }));

  const uniqueTags = [...new Set(allPosts.map((post: any) => post.frontmatter.tags).flat())];
}

它会逐一遍历每篇 Markdown 文章,并将每个标签数组合并成一个更大的数组。然后,它会Set根据找到的所有标签创建一个新的数组(以忽略重复的值)。最后,它会将该集合转换为一个数组(不重复),您可以使用该数组在页面上显示标签列表。

替换getStaticPaths函数return的值

return uniqueTags.map((tag) => {
  const filteredPosts = allPosts.filter((post: any) => post.frontmatter.tags.includes(tag));
  return {
    params: { tag },
    props: { posts: filteredPosts },
  };
});

现在,所有博客文章列表在作为 props 发送到每个页面之前都会经过过滤。请务必删除之前过滤文章的代码行,并更新 HTML 模板以posts使用filteredPosts

src/pages/tags/[tag].astro

html
---
const { tag } = Astro.params;
const { posts } = Astro.props;
---
<!-- -->
<ul>
  {posts.map((post: any) => <BlogPost url={post.url} title={post.frontmatter.title}/>)}
</ul>

最终代码:

src/pages/tags/[tag].astro

---
import BaseLayout from '../../layouts/BaseLayout.astro';
import BlogPost from '../../components/BlogPost.astro';

export async function getStaticPaths() {
  const allPosts = Object.values(import.meta.glob('../posts/*.md', { eager: true }));

  const uniqueTags = [...new Set(allPosts.map((post: any) => post.frontmatter.tags).flat())];

  return uniqueTags.map((tag) => {
    const filteredPosts = allPosts.filter((post: any) => post.frontmatter.tags.includes(tag));
    return {
      params: { tag },
      props: { posts: filteredPosts },
    };
  });
}

const { tag } = Astro.params;
const { posts } = Astro.props;
---
<BaseLayout pageTitle={tag}>
  <p>Posts tagged with {tag}</p>
  <ul>
    {posts.map((post: any) => <BlogPost url={post.url} title={post.frontmatter.title}/>)}
  </ul>
</BaseLayout>

建立tag索引页面

现在您已经为每个标签创建了单独的页面,是时候为它们创建链接了。

使用/pages/folder/index.astro路由模式

要将标签索引页面添加到您的网站,您创建一个新文件src/pages/tags.astro

但是,由于您已经有了目录/tags/,您可以利用 Astro 中的另一种路由模式,并将所有与标签相关的文件保存在一起。

访问/pages/tags对应

/pages/tags/index.astro

或者

/pages/tags.astro

Astro 的路由技巧

Astro(以及许多其他现代框架)允许你使用一个特殊的索引文件来作为父文件夹的默认页面

  • 如果你在 src/pages/tags/ 文件夹内创建一个名为 index.astro 的文件,那么这个文件就会被用作 /tags/ 路径的页面。

总结来说,它的建议是:

与其创建:

  • src/pages/tags.astro (标签索引页)
  • src/pages/tags/[tag].astro (单个标签页)

不如将标签索引页的文件放进 tags 文件夹内,命名为 index.astro,从而实现更清晰的组织:

  • src/pages/tags/index.astro (作为 /tags/ 页面)
  • src/pages/tags/[tag].astro (作为 /tags/some-tag/ 页面)

这样做的好处是将所有与标签相关的逻辑和页面文件都集中在同一个 src/pages/tags/ 目录下,让项目结构更易于管理。

创建标签数组

js
const allPosts = Object.values(import.meta.glob('../posts/*.md', { eager: true }));
const tags = [...new Set(allPosts.map((post: any) => post.frontmatter.tags).flat())];

要使每个标签链接到其自己的页面,请<a>向每个标签名称添加以下链接:

src/pages/tags/index.astro

<BaseLayout pageTitle={pageTitle}>
  <div>
    {tags.map((tag) => (
      <p><a href={`/tags/${tag}`}>{tag}</a></p>

    ))}
  </div>
</BaseLayout>

src/layouts/MarkdownPostLayout.astro

html
---
import BaseLayout from './BaseLayout.astro';
const { frontmatter } = Astro.props;
---
<BaseLayout pageTitle={frontmatter.title}>
  <p><em>{frontmatter.description}</em></p>
  <p>{frontmatter.pubDate.toString().slice(0,10)}</p>

  <p>Written by: {frontmatter.author}</p>

  <img src={frontmatter.image.url} width="300" alt={frontmatter.image.alt} />

  <div class="tags">
    {frontmatter.tags.map((tag: string) => (
      <p class="tag"><a href={`/tags/${tag}`}>{tag}</a></p>
    ))}
  </div>

  <slot />
</BaseLayout>
<style>
  a {
    color: #00539F;
  }

  .tags {
    display: flex;
    flex-wrap: wrap;
  }

  .tag {
    margin: 0.25em;
    border: dotted 1px #a1a1a1;
    border-radius: .5em;
    padding: .5em 1em;
    font-size: 1.15em;
    background-color: #F8FCFD;
  }
</style>

将每个文件路径与将在同一路由创建页面的第二个文件路径进行匹配。

src/pages/categories.astro

src/pages/categories/index.astro

src/pages/posts.astro

src/pages/posts/index.astro

添加RSS源

指的是在你的网站或博客中创建一个 RSS 文件,以允许用户通过 RSS 阅读器(RSS reader) 或其他聚合服务来订阅你的最新内容。

什么是 RSS?

RSS(Really Simple Syndication 或 Rich Site Summary)是一种 XML 文件格式,用于发布频繁更新的数字内容,例如博客文章、新闻或播客。

核心目的

它允许网站自动向订阅者推送一个结构化的、易于阅读的摘要或全文列表。

对你的网站来说意味着什么?

  1. 分发内容: 你不再需要用户不断访问你的网站来查看是否有新文章。新文章发布后,它会自动出现在 RSS 订阅源中。
  2. 增强用户体验: 忠实读者可以使用他们偏爱的 RSS 阅读器(比如 Feedly、Inoreader 等)在一个地方聚合阅读来自你和其他网站的所有最新内容。
  3. 提高可发现性: 一些聚合服务和目录会使用 RSS 源来查找和收录新内容,帮你扩大影响力。

Unit 6 - Astro Islands

现在您已经拥有一个功能齐全的博客,是时候为您的网站添加一些互动岛屿了!

在本单元中,您将使用Astro 岛将前端框架组件带入您的 Astro 网站。

你会:

  • 向您的 Astro 项目添加 UI 框架 Preact
  • 使用 Preact 创建交互式问候组件
  • 了解何时适合选择岛屿进行互动

your first Astro island

使用 vue 组件通过随机选择的欢迎消息迎接您的访客!

将 vue添加到您的 Astro 项目

  1. 使用单个命令即可在 Astro 项目中添加使用 Preact 组件的功能:

    npx astro add vue
  2. 按照命令行说明确认将 Vue 添加到您的项目中。

image-20251010175554549

编写组件:

src\pages\components\MyTestApp.vue

<template>
    <div>
        <h1>this is {{name}}</h1>
    </div>
</template>

<script setup>
const name = 'Vue3 in Astro'
</script>

<style lang="scss" scoped>

</style>

使用组件

src\pages\index.astro

---
import MyTestApp from './components/MyTestApp.vue';
---
<MyTestApp client:load />

运行结果:

image-20251010180238333

JS测试:

MyTestApp.vue

vue
<template>
    <div>
        <h1>this is {{name}}</h1>
        <button @click="handleClick">JS测试</button>
    </div>
</template>

<script setup>
const name = 'Vue3 in Astro'
function handleClick() {
    alert('Hello from Vue3 in Astro!')
}
</script>

<style lang="scss" scoped>

</style>

index.astro

html
---
import MyTestApp from './components/MyTestApp.vue';
---
<meta charset="UTF-8" />
<!-- 不添加clint:load -->
<MyTestApp />
<!-- 添加clint:load -->
<MyTestApp client:load />

第二个JS能正常使用,但是第一个点击无反应

二个按钮之所以有效,是因为client:load指令告诉 Astro在页面加载时将其 JavaScript 发送并重新运行到客户端,从而使组件具有交互性。这被称为“hydrated component”。

分析模式

还有其他client:指令可供探索。每个指令在不同的时间将 JavaScript 发送到客户端。例如client:visible, 仅当组件在页面上可见时才发送其 JavaScript。

如果你对一个astro文件使用,会产生

You are attempting to render <Navigation client:load />, but Navigation is an Astro component. Astro components do not render in the client and should not have a hydration directive. Please use a framework component for client rendering.

回到陆地上。从白天到晚上,都可以写博客,无需岛屿!

现在您可以为交互元素构建 Astro island,请不要忘记,仅使用原始 JavaScript 和 CSS 就可以走得很远!

让我们构建一个可点击的图标,让您的用户使用另一个标签在明暗模式之间切换<script>以实现交互……无需向浏览器发送任何框架 JavaScript。

  • 仅使用 JavaScript 和 CSS 构建交互式主题切换
  • 向浏览器发送尽可能少的 JavaScript!

在能够使用 原生 Web 技术 (Vanilla JS 和 CSS) 解决问题时,就应该优先使用原生技术,以确保你的网站:

  1. 加载更快: 没有额外的框架代码需要下载和解析。
  2. 更健壮: 依赖最基础的浏览器功能。

通过这个 主题切换 的例子,你将学会如何在 Astro 中高效地添加简单的客户端交互,同时保持页面的轻量快速

添加客户端交互

要为 Astro 组件添加交互功能,您可以使用<script>标签。此脚本可以检查并设置当前主题,localStorage并在点击图标时切换主题。

  1. 在您的标签后添加以下<script>标签:src/components/ThemeIcon.astro <style>
html
<style>
  .sun { fill: black; }
  .moon { fill: transparent; }

  :global(.dark) .sun { fill: transparent; }
  :global(.dark) .moon { fill: white; }
</style>

<script is:inline>
  const theme = (() => {
    const localStorageTheme = localStorage?.getItem("theme") ?? '';
    if (['dark', 'light'].includes(localStorageTheme)) {
      return localStorageTheme;
    }
    if (window.matchMedia('(prefers-color-scheme: dark)').matches) {
      return 'dark';
    }
      return 'light';
  })();

  if (theme === 'light') {
    document.documentElement.classList.remove('dark');
  } else {
    document.documentElement.classList.add('dark');
  }
  window.localStorage.setItem('theme', theme);
  const handleToggleClick = () => {
    const element = document.documentElement;
    element.classList.toggle("dark");

    const isDark = element.classList.contains("dark");
    localStorage.setItem("theme", isDark ? "dark" : "light");
  }

  document.getElementById("themeToggle")?.addEventListener("click", handleToggleClick);
</script>

当我不想添加框架时,我可以使用 JavaScript 进行交互。

:global(.dark):这告诉构建系统,要查找页面上全局的、带有 .dark 类的 <body><html> 元素(通常通过 JavaScript 动态添加)。

<script is:inline> 是 Astro 框架中特有的一个 HTML 属性(attribute),用于控制<script>标签内包含的 JavaScript 代码如何被处理和交付给浏览器。

它的意思是:

强制内联脚本 (Force Inlined Script)

当你在 Astro 文件(如 .astro.md 文件中的布局)里的 <script> 标签上添加 is:inline 属性时,你告诉 Astro:

  1. 不要处理: 不要将这个 <script> 标签内的代码视为一个模块(module),也不要对其进行任何优化、打包(bundling)或依赖图分析。
  2. 直接注入: 将这段 JavaScript 代码原样内联地(inlined)插入到最终生成的 HTML 页面的 <script> 标签内。

在 Astro 中,默认的 <script> 标签(没有 is:inline)会被视为 模块脚本

  • Astro 会处理这些脚本,将它们打包成单独的文件,并使用 type="module" 属性延迟加载,以优化性能。

但对于某些特定的需求,你必须使用 is:inline

场景为什么使用 is:inline示例
立即执行脚本需要在页面加载的早期阻塞式地执行,以避免闪烁(Flash of Unstyled Content, FOUC) 或设置关键的全局变量。深色/浅色模式切换的脚本,它必须在 CSS 加载前读取用户偏好并设置 <body> 上的 class
访问 DOM脚本需要同步访问或操作 HTML 中的元素,例如设置初始的 DOM 状态。用于主题切换的 JavaScript 代码。
第三方代码粘贴来自 Google Analytics、广告服务或字体加载器等第三方服务的追踪代码或嵌入脚本。任何要求你“直接粘贴到 <head>”的脚本。

Astro 推荐你在那些必须在页面加载初期执行绝对轻量的代码上使用 is:inline

项目结构

目录和文件

Astro 为您的项目采用了固定的文件夹布局。每个 Astro 项目根目录都应包含以下目录和文件:

  • src/*- 您的项目源代码(组件、页面、样式、图像等)
  • public/*- 您的非代码、未处理的资产(字体、图标等)
  • package.json- 项目清单。
  • astro.config.mjs- Astro 配置文件。(推荐)
  • tsconfig.json- TypeScript 配置文件。(推荐)

常见的 Astro 项目目录可能如下所示:

image-20251010222049851image-20251010222116485

src/

The src/ folder is where most of your project source code lives. This includes:

Astro 会处理、优化和打包您的src/文件,以创建最终发送到浏览器的网站。与静态public/目录不同,src/Astro 会为您构建和处理文件。

有些文件(例如 Astro 组件)甚至不会直接发送到浏览器,而是渲染为静态 HTML。其他文件(例如 CSS)虽然发送到浏览器,但可能会进行优化或与其他 CSS 文件捆绑以提高性能。

虽然本指南介绍了 Astro 社区中一些常用的约定,但 Astro 唯一保留的目录是src/pages/。您可以随意重命名和重新组织任何其他目录,以最适合您的方式。

src/pages

通过向此目录添加支持的文件类型,为您的网站创建页面路由。

Astro supports the following file types in the src/pages/ directory:

src/pages是Astro 项目中必需的子目录。没有它,您的网站将没有页面或路由!

src/components

组件是 HTML 页面中可复用的代码单元。这些组件可以是Astro 组件,也可以是像 React 或 Vue 这样的UI 框架组件。通常,将项目中的所有组件分组并组织到此文件夹中。

这是 Astro 项目中的常见惯例,但并非强制要求。您可以随意组织组件!

src/layouts

布局是 Astro 组件,用于定义一个或多个页面共享的 UI 结构。

就像src/components,这个目录是一个常见的约定,但不是必需的。

src/styles

通常将 CSS 或 Sass 文件存储在src/styles目录中,但这并非强制要求。只要您的样式位于src/目录中的某个位置并正确导入,Astro 就会处理并优化它们。

public/

public/目录用于存放项目中不需要在 Astro 构建过程中处理的文件和资源。此文件夹中的文件将被原封不动地复制到构建文件夹中,然后 Astro 就会构建您的网站。

这种行为public/非常适合不需要任何处理的常见资产,例如某些图像和字体,或特殊文件(如robots.txtmanifest.webmanifest)。

您可以将 CSS 和 JavaScript 放在public/目录中,但请注意,这些文件不会在最终版本中捆绑或优化。

一般来说,您自己编写的任何 CSS 或 JavaScript 都应该位于您的src/目录中。

package.json

这是 JavaScript 包管理器用来管理依赖项的文件。它还定义了运行 Astro 的常用脚本(例如:npm run devnpm run build)。

astro.config.mjs

每个入门模板都会生成此文件,其中包含 Astro 项目的配置选项。您可以在此处指定要使用的集成、构建选项、服务器选项等。

Astro 的 JavaScript 配置文件支持多种文件格式:astro.config.js、和。在大多数情况下,或者如果您想在配置文件中编写 TypeScript,astro.config.mjs我们建议使用。astro.config.cjs``astro.config.ts``.mjs``.ts

TypeScript 配置文件加载使用来处理tsm并将尊重您的项目的tsconfig选项。

请参阅配置参考以了解完整详细信息。

tsconfig.json

此文件在每个入门模板中都会生成,其中包含 Astro 项目的 TypeScript 配置选项。某些功能(例如 npm 包导入)如果没有此tsconfig.json文件,则在编辑器中无法完全支持。

完整笔记

Astro-服务器渲染.md

Astro-路由导航.md

Astro-构建组件.md

Astro-可添加内容.md

pages vs content collection

简单来说: 👉 src/pages/\*.md 更简单、更传统; 👉 src/content/collections 更强大、更结构化

src/pages/*.md 方式

这种方式最直接: 你把 .md 文件放在 src/pages 下,它就自动变成一个页面路由。

优点

  • 零配置:放文件即可访问 /xxx 路径。
  • 写博客/静态页面简单:例如 src/pages/about.md 自动生成 /about
  • 支持前置 frontmatter,同样可以用 layout

缺点

  • 无法灵活查询或遍历:Astro 不会自动帮你索引这些页面。

    比如想列出所有博客文章标题 → 你要手动 import.meta.glob("/src/pages/blog/*.md")

  • 不支持类型验证:frontmatter 没有类型检查。

  • 难以组织大规模内容(例如分类、标签、作者、多语言)。

适用场景

  • 小型网站(比如公司官网、文档少、几页内容)。
  • 没有太复杂的内容结构。
  • 不在意类型安全或统一 frontmatter 格式。

src/content/collections 方式(Content Collections)

Astro 2.0 之后推荐的做法。 你可以定义一个 src/content/config.ts,为不同内容(blog、docs、projects)定义 schema。

优点

  • 有类型安全:自动生成 TypeScript 类型。
  • 可结构化查询getCollection('blog') 拿到所有博客。
  • 可定义校验规则:比如每篇文章必须有 titledatetags
  • 易扩展:未来迁移到 CMS 或多语言时特别方便。

缺点

  • pages 稍复杂(需要配置 config.ts)。
  • 默认不会自动生成路由(需要自己写 [slug].astro 模板)。

适用场景

  • 博客 / 文档 / 项目展示网站
  • 内容较多、有分类或元数据需求。
  • 追求类型安全、统一格式。
  • 想做 RSS / 搜索 / 标签页 等功能。

slug vs id

id 是「文件名(带路径)」

id = 文件的相对路径 + 扩展名

它是 内容集合内部的唯一标识符,严格匹配文件系统路径。

举个例子:

src/content/docs/
├── ajax.md
├── fetch/
│   └── index.md
└── http/
    └── intro.md

则对应的 id 值分别是:

文件路径id
src/content/docs/ajax.md"ajax.md"
src/content/docs/fetch/index.md"fetch/index.md"
src/content/docs/http/intro.md"http/intro.md"

💡 特点:

  • 带扩展名 .md
  • 带层级目录
  • 唯一、严格对应文件路径
  • 主要用于在内部操作时确保精确匹配

🪄 二、slug 是「URL 友好的标识」

slug = 文件路径(不带扩展名)

它主要用于生成 URL、动态路由、页面链接。

对应上面的例子:

文件路径slug
src/content/docs/ajax.md"ajax"
src/content/docs/fetch/index.md"fetch"
src/content/docs/http/intro.md"http/intro"

💡 特点:

  • 去掉 .md
  • index.md → 父目录名(fetch
  • 用于动态路由 ([slug].astro)
  • 更直观地反映 URL 路径

评论

评论加载中……