视图过渡
视图过渡(View transition)是网站不同视图之间的动画过渡。它们是一种流行的设计选择,用于在访问者在应用程序的状态或视图之间移动时保持视觉连续性。
Astro 的视图过渡和客户端路由支持由 View Transitions 浏览器 API 提供支持,并且还包括:
- 一些内置的动画选项,例如
fade
、slide
和none
。 - 支持前进和后退导航动画。
- 能够完全自定义过渡动画的所有方面,并构建自己的动画。
- 在导航期间将 HTML 元素从当前页面带到下一个页面的方式。
- 为非页面链接阻止客户端导航的选项。
- 为尚不支持 View Transition API 的浏览器提供回退行为的控制。
- 自动支持
prefers-reduced-motion
。
默认情况下,每个页面都将使用常规的、全页的浏览器导航。你必须选择性加入视图过渡,并且可以按页面或全站范围使用它们。
浏览器原生视图过渡与 Astro 的 <ClientRouter />
的区别
标题为“浏览器原生视图过渡与 Astro 的 <ClientRouter /> 的区别”的部分浏览器原生的跨文档视图过渡可用于 Astro,为多页应用(MPA)中的文档间导航添加动画,通常能提供单页应用(SPA)的客户端路由体验。它们不会改变多页应用的核心功能,也不会影响任何现有脚本或向你的页面加载中添加额外的 JavaScript。它们只是添加了动画。
对于 View Transition API 尚不完全支持的增强型客户端路由和视图过渡功能,Astro 提供了一个内置的轻量级组件,以启用客户端路由,并将你的多页应用转变为具有平滑导航动画的单页应用。
这带来了一些好处,比如跨页面共享状态和持久化元素,也有一些缺点,比如需要在导航后手动重新初始化脚本或状态。
添加 Astro 的内置 <ClientRouter />
组件
然而,随着浏览器 API 和 Web 标准的演进,使用 Astro 的 <ClientRouter />
来实现这些额外功能的必要性将日益降低。我们建议你跟上浏览器 API 的最新状态,以便你可以决定是否仍然需要 Astro 的客户端路由来支持你使用的特定功能。
启用视图过渡(SPA 模式)
标题为“启用视图过渡(SPA 模式)”的部分导入 <ClientRouter />
组件并将其添加到你的通用 <head>
或共享布局组件中。Astro 将根据新旧页面之间的相似性创建默认的页面动画,并为不支持的浏览器提供回退行为。
下面的示例通过导入并将此组件添加到 <CommonHead />
Astro 组件中,展示了在全站范围内添加 Astro 的默认页面导航动画,包括为不支持的浏览器提供的默认回退控制选项。
---import { ClientRouter } from "astro:transitions";---<link rel="icon" type="image/svg+xml" href="/favicon.svg" /><meta name="generator" content={Astro.generator} />
<!-- Primary Meta Tags --><title>{title}</title><meta name="title" content={title} /><meta name="description" content={description} />
<ClientRouter />
要启用 Astro 的默认客户端导航,无需其他配置!
在单个元素上使用过渡指令或覆盖默认的客户端导航以进行更精细的控制。
过渡指令
标题为“过渡指令”的部分Astro 会自动为旧页面和新页面中找到的相应元素分配一个共享且唯一的 view-transition-name
。这对匹配的元素是根据元素的类型及其在 DOM 中的位置推断出来的。
在你 .astro
组件中的页面元素上使用可选的 transition:*
指令,以便在导航期间对页面过渡行为进行更精细的控制。
transition:name
: 允许你覆盖 Astro 的默认元素匹配,用于新/旧内容动画,并指定一个过渡名称来关联一对 DOM 元素。transition:animate
: 允许你在用新元素替换旧元素时,通过指定一种动画类型来覆盖 Astro 的默认动画。使用 Astro 的内置动画指令或创建自定义过渡动画。transition:persist
: 允许你覆盖 Astro 的默认行为(用新元素替换旧元素),而是在导航到另一个页面时持久化组件和 HTML 元素。
命名过渡
标题为“命名过渡”的部分在某些情况下,你可能想要或需要自己识别相应的视图过渡元素。你可以使用 transition:name
指令为一对元素指定名称。
<aside transition:name="hero">
<aside transition:name="hero">
请注意,提供的 transition:name
值在每个页面上只能使用一次。当 Astro 无法自行推断出正确的名称时,或者为了更精细地控制匹配元素时,可以手动设置此项。
维护状态
标题为“维护状态”的部分
新增于: astro@2.10.0
你可以使用 transition:persist
指令在页面导航之间持久化组件和 HTML 元素(而不是替换它们)。
例如,当你导航到包含相同视频元素的另一个页面时,下面的 <video>
将继续播放。这对于前进和后退导航都有效。
<video controls muted autoplay transition:persist> <source src="https://ia804502.us.archive.org/33/items/GoldenGa1939_3/GoldenGa1939_3_512kb.mp4" type="video/mp4" /></video>
你也可以将该指令放在一个Astro island(一个带有 client:
指令的 UI 框架组件)上。如果该组件存在于下一个页面上,旧页面的 island 及其当前状态 将继续显示,而不是被新页面的 island 替换。
在下面的示例中,当在包含带有 transition:persist
属性的 <Counter />
组件的页面之间来回导航时,组件的内部计数值状态不会被重置。
<Counter client:load transition:persist initialCount={5} />
并非所有状态都可以通过这种方式保留。即使使用 transition:persist
,在视图过渡期间也无法避免 CSS 动画的重新启动和 iframe 的重新加载。
如果 island/元素在两个页面之间位于不同的组件中,你也可以手动识别相应的元素。
<video controls muted autoplay transition:name="media-player" transition:persist/>
<MyVideo controls muted autoplay transition:name="media-player" transition:persist/>
作为一种方便的简写,transition:persist
也可以接受一个过渡名称作为值。
<video controls muted autoplay transition:persist="media-player">
transition:persist-props
标题为“transition:persist-props”的部分
新增于: astro@4.5.0
这允许你控制一个 island 的 props 是否应该在导航时被持久化。
默认情况下,当你向 island 添加 transition:persist
时,状态会在导航时保留,但你的组件会用新的 props 重新渲染。这很有用,例如,当一个组件接收到特定于页面的 props,比如当前页面的 title
。
你可以通过在 transition:persist
之外再设置 transition:persist-props
来覆盖此行为。添加此指令将保留 island 现有的 props(不使用新值重新渲染),同时保持其现有状态。
内置动画指令
标题为“内置动画指令”的部分Astro 自带一些内置动画来覆盖默认的 fade
过渡。向单个元素添加 transition:animate
指令以自定义特定过渡的行为。
fade
(默认): 一种有主见的交叉淡入淡出动画。旧内容淡出,新内容淡入。initial
:选择退出 Astro 的主观交叉淡入淡出动画,并使用浏览器的默认样式。slide
: 一种动画,旧内容向左滑出,新内容从右滑入。在后退导航时,动画是相反的。none
: 禁用浏览器的默认动画。在页面的<html>
元素上使用,可以为页面上的每个元素禁用默认的淡入淡出效果。
组合指令以完全控制你的页面动画。在 <html>
元素上设置页面默认值,并根据需要覆盖任何单个元素。
下面的示例为正文内容生成了滑动动画,同时禁用了页面其余部分的浏览器默认淡入淡出动画
---import CommonHead from "../components/CommonHead.astro";---
<html transition:name="root" transition:animate="none"> <head> <CommonHead /> </head> <body> <header> ... </header> <!-- Override your page default on a single element --> <main transition:animate="slide"> ... </main> </body></html>
自定义动画
标题为“自定义动画”的部分你可以使用任何 CSS 动画属性自定义过渡的各个方面。
要自定义内置动画,首先从 astro:transitions
导入动画,然后传入自定义选项。
下面的示例自定义了内置 fade
动画的持续时间
---import { fade } from "astro:transitions";---<header transition:animate={fade({ duration: "0.4s" })}>
你还可以通过定义前进和后退行为,以及新旧页面,根据以下类型为 transition:animate
定义你自己的动画
export interface TransitionAnimation { name: string; // The name of the keyframe delay?: number | string; duration?: number | string; easing?: string; fillMode?: string; direction?: string;}
export interface TransitionAnimationPair { old: TransitionAnimation | TransitionAnimation[]; new: TransitionAnimation | TransitionAnimation[];}
export interface TransitionDirectionalAnimations { forwards: TransitionAnimationPair; backwards: TransitionAnimationPair;}
以下示例展示了在你的根布局文件的 <style is:global>
标签内定义自定义 bump
动画所需的所有属性
---import { ClientRouter } from "astro:transitions";---<html lang="en"> <head> <ClientRouter /> </head> <body> <slot /> </body></html>
<style is:global> @keyframes bump { 0% { opacity: 0; transform: scale(1) translateX(200px); } 50% { opacity: 0.5; transform: scale(1.1); } 100% { opacity: 1; transform: scale(1) translateX(0); } }</style>
动画的行为必须在使用该动画的每个组件的 frontmatter 中定义
---const anim = { old: { name: "bump", duration: "0.5s", easing: "ease-in", direction: "reverse", }, new: { name: "bump", duration: "0.5s", easing: "ease-in-out", },};
const customTransition = { forwards: anim, backwards: anim,};---<header transition:animate={customTransition}> ... </header>
在定义自定义动画时,你具有很大的灵活性。为了达到你想要的效果,你可能希望考虑一些不寻常的组合,例如为前进和后退使用不同的对象,或为新旧元素提供单独的关键帧动画。
路由控制
标题为“路由控制”的部分<ClientRouter />
路由器通过监听以下事件来处理导航:
- 点击
<a>
元素。 - 后退和前进导航事件。
以下选项允许你进一步控制路由器内的导航何时发生:
data-astro-reload
: 一个<a>
标签属性,用于强制进行全页导航data-astro-history="auto | push | replace"
: 一个<a>
标签属性,用于控制浏览器的历史记录navigate(href, options)
: 一个可用于任何客户端脚本或客户端组件的方法,用于触发导航
阻止客户端导航
标题为“阻止客户端导航”的部分在某些情况下,你无法通过客户端路由进行导航,因为所涉及的两个页面都必须使用 <ClientRouter />
路由器才能防止全页重新加载。你可能也不希望在每次导航更改时都使用客户端路由,而是希望在选定的路由上使用传统的页面导航。
你可以通过向任何 <a>
或 <form>
标签添加 data-astro-reload
属性来选择性地退出客户端路由。此属性将覆盖任何现有的 <ClientRouter />
组件,并在导航期间触发浏览器刷新。
以下示例展示了仅在从主页导航到文章时阻止客户端路由。这仍然允许你在从文章列表页面导航到同一页面时,在共享元素(如英雄图片)上使用动画。
<a href="/articles/emperor-penguins" data-astro-reload>
<a href="/articles/emperor-penguins">
带有 data-astro-reload
属性的链接将被路由器忽略,并发生全页导航。
触发导航
标题为“触发导航”的部分你还可以通过 <ClientRouter />
路由器通常不监听的事件来触发客户端导航,使用 navigate
。这个来自 astro:transitions/client
模块的函数可以在脚本和使用客户端指令进行水合的框架组件中使用。
以下示例展示了一个 Astro 组件,该组件将访问者导航到他们从菜单中选择的另一个页面。
<script> import { navigate } from "astro:transitions/client";
// Navigate to the selected option automatically. document.querySelector("select").onchange = (event) => { let href = event.target.value; navigate(href); };</script><select> <option value="/play">Play</option> <option value="/blog">Blog</option> <option value="/about">About</option> <option value="/contact">Contact</option></select>
---import Form from "../components/Form.astro";import { ClientRouter } from "astro:transitions";---<html> <head> <ClientRouter /> </head> <body> <Form /> </body></html>
以下示例在 React <Form />
组件中使用了 navigate()
实现了相同的功能
import { navigate } from "astro:transitions/client";
export default function Form() { return ( <select onChange={(e) => navigate(e.target.value)}> <option value="/play">Play</option> <option value="/blog">Blog</option> <option value="/about">About</option> <option value="/contact">Contact</option> </select> );}
<Form />
组件随后可以渲染在使用 <ClientRouter />
路由器的 Astro 页面上,并带有一个客户端指令
---import Form from "../components/Form.jsx";import { ClientRouter } from "astro:transitions";---<html> <head> <ClientRouter /> </head> <body> <Form client:load /> </body></html>
navigate
方法接受以下参数
href
(必需) - 要导航到的新页面。options
- 一个可选对象,具有以下属性history
:"push"
|"replace"
|"auto"
"push"
: 路由器将使用history.pushState
在浏览器历史记录中创建一个新条目。"replace"
: 路由器将使用history.replaceState
来更新 URL,而不在导航中添加新条目。"auto"
(默认): 路由器将尝试history.pushState
,但如果 URL 无法转换,当前 URL 将保持不变,浏览器历史记录也不会有任何更改。
formData
: 用于POST
请求的FormData
对象。
对于通过浏览器历史记录进行的前进和后退导航,你可以将 navigate()
与浏览器的内置函数 history.back()
、history.forward()
和 history.go()
结合使用。如果在组件的服务器端渲染期间调用 navigate()
,它将不起作用。
替换浏览器历史记录中的条目
标题为“替换浏览器历史记录中的条目”的部分通常,每次导航时,都会向浏览器的历史记录中写入一个新条目。这允许使用浏览器的 back
和 forward
按钮在页面之间导航。
<ClientRouter />
路由器允许你通过向任何单个 <a>
标签添加 data-astro-history
属性来覆盖历史记录条目。
data-astro-history
属性可以设置为与 navigate()
函数的 history
选项相同三个值
data-astro-history
: "push"
| "replace"
| "auto"
"push"
: 路由器将使用history.pushState
在浏览器历史记录中创建一个新条目。"replace"
: 路由器将使用history.replaceState
来更新 URL,而不在导航中添加新条目。"auto"
(默认): 路由器将尝试history.pushState
,但如果 URL 无法转换,当前 URL 将保持不变,浏览器历史记录也不会有任何更改。
以下示例导航到 /main
页面,但不会在浏览历史中添加新条目。相反,它会重用历史中的当前条目(/confirmation
)并覆盖它。
<a href="/main" data-astro-history="replace">
这样做的效果是,如果你从 /main
页面返回,浏览器将不会显示 /confirmation
页面,而是显示它之前的页面。
表单过渡
标题为“表单过渡”的部分
新增于: astro@4.0.0
<ClientRouter />
路由器将从 <form>
元素触发页内过渡,支持 GET
和 POST
请求。
默认情况下,当你的 method
设置为 POST
时,Astro 会将你的表单数据作为 multipart/form-data
提交。如果你想匹配 Web 浏览器的默认行为,请使用 enctype
属性将你的数据编码为 application/x-www-form-urlencoded
进行提交
<form action="/contact" method="POST" enctype="application/x-www-form-urlencoded">
你可以使用 data-astro-reload
属性在任何单个表单上选择退出路由器过渡。
<form action="/contact" data-astro-reload>
回退控制
标题为“回退控制”的部分<ClientRouter />
路由器在支持视图过渡的浏览器(即 Chromium 浏览器)中工作得最好,但也为其他浏览器提供了默认的回退支持。即使浏览器不支持 View Transitions API,Astro 的客户端路由器仍然可以使用其中一个回退选项提供浏览器内导航。
根据浏览器的支持情况,你可能需要明确设置 name
或 animate
过渡指令,以便在所有浏览器中获得可比较的体验。
---import Layout from "../layouts/LayoutUsingClientRouter.astro";---<title transition:animate="fade">About my site</title>
你可以通过在 <ClientRouter />
组件上添加 fallback
属性并将其设置为 swap
或 none
来覆盖 Astro 的默认回退支持
animate
(默认,推荐): Astro 将在更新页面内容之前使用自定义属性模拟视图过渡。swap
:Astro 不会尝试为页面设置动画。相反,旧页面将立即被新页面替换。none
: Astro 将根本不进行任何动画页面过渡。相反,你将在不支持的浏览器中获得完整的页面导航。
---import { ClientRouter } from "astro:transitions";---<title>My site</title>
<ClientRouter fallback="swap" />
initial
浏览器动画不由 Astro 模拟。所以任何使用此动画的元素目前都不会有动画效果。
客户端导航过程
标题为“客户端导航过程”的部分当使用 <ClientRouter />
路由器时,会发生以下步骤来产生 Astro 的客户端导航:
-
网站的访问者通过以下任一操作触发导航:
- 点击链接到你网站上另一个页面的内部
<a>
标签。 - 点击后退按钮。
- 点击前进按钮。
- 点击链接到你网站上另一个页面的内部
-
路由器开始获取下一个页面。
-
路由器将
data-astro-transition
属性添加到 HTML 元素,其值适当地设为"forward"
或"back"
。 -
路由器调用
document.startViewTransition
。这会触发浏览器自身的视图过渡过程。重要的是,浏览器会对当前页面的状态进行截图。 -
在
startViewTransition
回调函数内部,路由器会执行一次 swap,它由以下一系列事件组成:-
<head>
的内容被换出,但一些元素被保留:- 如果新页面上存在样式表 DOM 节点,它们将被保留,以防止 FOUC(无样式内容闪烁)。
- 如果新页面上存在脚本,它们将被保留。
- 如果新页面中有相应的元素,任何其他带有
transition:persist
的 head 元素都将被保留。
-
<body>
会被完全替换为新页面的 body。 -
如果标记为
transition:persist
的元素存在于新页面上,它们将被移动到新的 DOM 中。 -
如有必要,滚动位置会被恢复。
-
astro:after-swap
事件在document
上被触发。这是 swap 过程的结束。
-
-
在解析转换之前,路由器会等待任何新的样式表加载完成。
-
路由器执行添加到页面的任何新脚本。
-
astro:page-load
事件触发。这是导航过程的结束。
视图过渡中的脚本行为
标题为“视图过渡中的脚本行为”的部分当您向现有的 Astro 项目添加视图过渡时,您的一些脚本可能在页面导航后不再像在全页浏览器刷新时那样重新运行。请使用以下信息来确保您的脚本按预期执行。
脚本顺序
标题为“脚本顺序”的部分当使用 <ClientRouter />
组件在页面之间导航时,脚本会按顺序运行,以匹配浏览器的行为。
脚本重新执行
标题为“脚本重新执行”的部分打包的模块脚本(Astro 中的默认脚本)只会被执行一次。在初次执行后,它们将被忽略,即使在转换后新页面上存在该脚本。
与打包的模块脚本不同,内联脚本有可能在用户访问网站期间被重新执行,如果它们存在于被多次访问的页面上。当访问者导航到一个没有该脚本的页面,然后又返回到一个有该脚本的页面时,内联脚本也可能重新执行。
使用视图过渡时,某些脚本在页面导航后可能不再像在全页浏览器刷新时那样重新运行。在客户端路由期间,有几个可以监听的事件,并且在它们发生时触发事件。您可以将现有脚本包装在事件监听器中,以确保它在导航周期中的正确时间运行。
以下示例将用于移动端“汉堡”菜单的脚本包装在 astro:page-load
的事件监听器中,该事件在页面导航结束时运行,以使菜单在导航到新页面后响应点击。
document.addEventListener("astro:page-load", () => { document.querySelector(".hamburger").addEventListener("click", () => { document.querySelector(".nav-links").classList.toggle("expanded"); });});
以下示例展示了一个响应 astro:after-swap
事件运行的函数,该事件在旧页面被新页面替换后立即发生,但在 DOM 元素被绘制到屏幕之前。这通过在新页面渲染前检查并(如果需要)设置暗黑模式主题,避免了页面导航后亮色主题的闪烁。
<script is:inline> function applyTheme() { localStorage.theme === "dark" ? document.documentElement.classList.add("dark") : document.documentElement.classList.remove("dark"); }
document.addEventListener("astro:after-swap", applyTheme); applyTheme();</script>
data-astro-rerun
标题为“data-astro-rerun”的部分
新增于: astro@4.5.0
要强制内联脚本在每次转换后重新执行,请添加 data-astro-rerun
属性。向脚本添加任何属性也会隐式添加 is:inline
,因此这仅适用于未被 Astro 打包和处理的脚本。
<script is:inline data-astro-rerun>...</script>
为确保脚本在客户端导航期间每次加载页面时都运行,它应该由一个生命周期事件来执行。例如,DOMContentLoaded
的事件监听器可以被astro:page-load
生命周期事件替换。
如果你的代码在内联脚本中设置了全局状态,这个状态需要考虑到脚本可能会执行多次的情况。在你的 <script>
标签中检查全局状态,并尽可能有条件地执行你的代码。这是可行的,因为 window
是被保留的。
<script is:inline> if (!window.SomeGlobal) { window.SomeGlobal = {}; }</script>
生命周期事件
标题为“生命周期事件”的部分<ClientRouter />
路由器在导航期间会在 document
上触发多个事件。这些事件提供了导航生命周期的钩子,允许你执行诸如显示新页面正在加载的指示器、覆盖默认行为以及在导航完成时恢复状态等操作。
导航过程包括一个**准备**阶段,此时加载新内容;一个 **DOM 交换**阶段,此时旧页面的内容被新页面的内容替换;以及一个**完成**阶段,此时执行脚本,报告加载完成并执行清理工作。
Astro 的视图过渡 API 生命周期事件顺序如下:
before-
事件允许你影响和修改即将发生的操作,而 after-
事件是阶段完成的通知。
虽然某些操作可以在任何事件期间触发,但有些任务只能在特定事件期间执行才能获得最佳结果,例如在准备前显示加载指示器或在交换内容前覆盖动画对。
astro:before-preparation
标题为“astro:before-preparation”的部分
新增于: astro@3.6.0
在准备阶段开始时触发的事件,在导航开始后(例如,用户点击链接后),但在加载内容之前。
此事件用于:
- 在加载开始前做些什么,比如显示一个加载指示器。
- 改变加载行为,例如加载你在模板中定义的内容,而不是从外部 URL 加载。
- 为自定义动画更改导航的
direction
(通常是forward
或backward
)。
这是一个使用 astro:before-preparation
事件在内容加载前加载一个指示器,并在加载后立即停止它的示例。请注意,以这种方式使用 loader
回调允许异步执行代码。
<script is:inline> document.addEventListener("astro:before-preparation", (event) => { const originalLoader = event.loader; event.loader = async function () { const { startSpinner } = await import("./spinner.js"); const stop = startSpinner(); await originalLoader(); stop(); }; });</script>
astro:after-preparation
标题为“astro:after-preparation”的部分
新增于: astro@3.6.0
在准备阶段结束时,在新页面的内容被加载并解析成文档后触发的事件。此事件在视图过渡阶段之前发生。
此示例使用 astro:before-preparation
事件启动加载指示器,并使用 astro:after-preparation
事件停止它。
<script is:inline> document.addEventListener("astro:before-preparation", () => { document.querySelector("#loading").classList.add("show"); }); document.addEventListener("astro:after-preparation", () => { document.querySelector("#loading").classList.remove("show"); });</script>
这是一个比上面示例更简单的加载指示器版本:如果监听器的所有代码都可以同步执行,则无需挂接到 loader
回调。
astro:before-swap
标题为“astro:before-swap”的部分
新增于: astro@3.6.0
在新文档(在准备阶段填充)替换当前文档之前触发的事件。此事件发生在视图过渡内部,此时用户仍然看到旧页面的快照。
此事件可用于在交换发生前进行更改。事件上的 newDocument
属性表示传入的文档。这是一个确保浏览器在 localStorage
中的亮色或暗色模式偏好被带到新页面的示例:
<script is:inline> function setDarkMode(document) { let theme = localStorage.darkMode ? "dark" : "light"; document.documentElement.dataset.theme = theme; }
setDarkMode(document);
document.addEventListener("astro:before-swap", (event) => { // Pass the incoming document to set the theme on it setDarkMode(event.newDocument); });</script>
astro:before-swap
事件也可用于更改交换的*实现*。默认的交换实现会差异化 head 内容,将**持久化**元素从旧文档移动到 newDocument
,然后用新文档的 body 替换整个 body
。
在生命周期的这个节点,你可以选择定义自己的交换实现,例如,对现有文档的全部内容进行差异化比较(其他一些路由器是这样做的):
<script is:inline> document.addEventListener("astro:before-swap", (event) => { event.swap = () => { diff(document, event.newDocument); }; });</script>
构建自定义交换函数
标题为“构建自定义交换函数”的部分
新增于: astro@4.15.0
astro:transitions/client
模块的 swapFunctions
对象提供了五个实用函数,用于处理特定的交换相关任务,包括处理文档属性、页面元素和脚本执行。这些函数可以直接用于定义自定义的交换实现。
以下示例演示了如何使用这些函数来重新创建 Astro 内置的交换实现:
<script> import { swapFunctions } from "astro:transitions/client";
// substitutes `window.document` with `doc` function mySwap(doc: Document) { swapFunctions.deselectScripts(doc); swapFunctions.swapRootAttributes(doc); swapFunctions.swapHeadElements(doc); const restoreFocusFunction = swapFunctions.saveFocus(); swapFunctions.swapBodyElement(doc.body, document.body); restoreFocusFunction(); }
document.addEventListener("astro:before-swap", (event) => { event.swap = () => mySwap(event.newDocument); });<script>
自定义交换实现可以从此模板开始,并根据需要添加或替换个别步骤为自定义逻辑。
astro:after-swap
标题为“astro:after-swap”的部分在新页面替换旧页面后立即触发的事件。你可以在 document
上监听此事件,并触发在新页面的 DOM 元素渲染和脚本运行之前发生的操作。
当在**传出页面**上监听此事件时,它对于传递和恢复需要在新页面上传递的 DOM 上的任何状态非常有用。
这是生命周期中仍然可以安全地添加暗黑模式类名(<html class="dark-mode">
)的最新时间点,尽管你可能希望在更早的事件中这样做。
astro:after-swap
事件在浏览器历史记录更新和滚动位置设置后立即发生。因此,针对此事件的一个用途是覆盖历史导航的默认滚动恢复。以下示例为每次导航将水平和垂直滚动位置重置到页面的左上角。
document.addEventListener("astro:after-swap", () => window.scrollTo({ left: 0, top: 0, behavior: "instant" }),);
astro:page-load
标题为“astro:page-load”的部分在页面导航结束时,新页面对用户可见且阻塞的样式和脚本加载完毕后触发的事件。你可以在 document
上监听此事件。
<ClientRouter />
组件在预渲染页面的初始页面导航和任何后续导航(无论是前进还是后退)时都会触发此事件。
你可以使用此事件在每次页面导航时运行代码,例如设置在导航期间否则会丢失的事件监听器。
<script> document.addEventListener("astro:page-load", () => { // This runs on first page load and after every navigation. setupStuff(); // e.g. add event listeners });</script>
无障碍性
标题为“无障碍性”的部分启用客户端路由和页面过渡动画都带来了无障碍性挑战,Astro 旨在使选择加入视图过渡的网站尽可能地默认无障碍。
路由宣告
标题为“路由宣告”的部分
新增于: astro@3.2.0
<ClientRouter />
组件包括一个用于在客户端路由期间进行页面导航的路由宣告器。无需配置或操作即可启用此功能。
辅助技术通过在导航后播报新页面的标题,让访问者知道页面已经更改。在使用服务器端路由和传统的全页浏览器刷新时,这在新页面加载后默认发生。在客户端路由中,<ClientRouter />
组件执行此操作。
为了向客户端路由添加路由宣告,该组件会在新页面上添加一个元素,其 aria-live
属性设置为 assertive
。这告诉 AT(辅助技术)立即宣告。该组件还会按优先级顺序检查以下内容,以确定宣告文本:
- 如果存在
<title>
,则使用它。 - 它找到的第一个
<h1>
。 - 页面的
pathname
。
为了无障碍性,我们强烈建议您始终在每个页面中包含一个 <title>
。
prefers-reduced-motion
标题为“prefers-reduced-motion”的部分Astro 的 <ClientRouter />
组件包含一个 CSS 媒体查询,每当检测到 prefer-reduced-motion
设置时,它会禁用*所有*视图过渡动画,包括回退动画。相反,浏览器将简单地交换 DOM 元素而没有动画。