视图过渡 View Transitions(ClientRouter)
本教程共 56 篇 · 第 32 篇 · 更新于 2026-08-07 · 约 10 分钟阅读
本节目标:学会用 v7 的
<ClientRouter />组件开启丝滑的页面过渡动画与客户端路由,知道它和已弃用的<ViewTransitions />有什么区别。
什么是视图过渡
视图过渡(View Transitions)是不同页面视图之间的动画过渡。它能让访客在页面或状态间切换时,视觉保持连续,体验更顺。Astro 的视图过渡与客户端路由由浏览器原生的 View Transitions API 驱动,还自带:
- 几种内置动画:
fade(淡入淡出)、slide(滑动)、none(无)。 - 支持前进和后退两种导航动画。
- 可完全自定义动画,也能自己写。
- 能把当前页的 HTML 元素「带」到下一页。
- 能对非页面链接关闭客户端导航。
- 对不支持该 API 的浏览器有回退方案。
- 自动尊重
prefers-reduced-motion(减少动画偏好)。
重要:v7 用 <ClientRouter />,别再用旧的
在 v4 之前,Astro 用的是 <ViewTransitions /> 组件。从 v4 起它改名成 <ClientRouter />,旧的 <ViewTransitions /> 已经被弃用,不要再写。本章全部示例都用新写法。
---
import { ClientRouter } from "astro:transitions";
---
<ClientRouter />
Note浏览器原生的「跨文档视图过渡」也能在 Astro 里用,给多页应用(MPA)的页面间加动画。它不改动 MPA 核心功能,也不额外加 JS,只是加动画。而
<ClientRouter />提供的是增强的客户端路由,把多页应用变成带平滑动画的单页应用(SPA)。
加上 <ClientRouter /> 后,它会拦截页面导航、扩展并增强部分 View Transition / Navigation API 的能力,还能为「原生浏览器不支持」的情况配置回退策略。代价是:导航后需要手动重新初始化脚本或状态。
开启视图过渡(SPA 模式)
只需把 <ClientRouter /> 导入并加进公共的 <head> 或共享布局组件。Astro 会根据新旧页面的相似度自动生成默认动画,也会为不支持的浏览器提供回退。
下面把默认导航动画开到全站,加在 CommonHead.astro 里:
---
import { ClientRouter } from "astro:transitions";
type Props = { title: string; description: string };
const { title, description } = Astro.props;
---
<link rel="icon" type="image/svg+xml" href="/favicon.svg" />
<meta name="generator" content={Astro.generator} />
<title>{title}</title>
<meta name="title" content={title} />
<meta name="description" content={description} />
<ClientRouter />
除此之外不用任何配置,Astro 的默认客户端导航就生效了。想要更细的控制,再往下看过渡指令。
过渡指令 transition:*
Astro 会自动给新旧页面里「对应」的元素分配一个共享的、唯一的 view-transition-name。这套配对由元素类型和在 DOM 里的位置推断出来。你可以用 transition:* 指令精细控制:
transition:name:覆盖默认的配对,手动指定两个元素之间的过渡名。transition:animate:覆盖默认动画,换用内置或自定义动画。transition:persist:不替换旧元素,而是把组件或 HTML 元素「保留」到下一页。
给过渡命名
有时你想自己指定配对元素,用 transition:name 给一对元素起同样的名字:
<!-- old-page.astro -->
<aside transition:name="hero">
<!-- new-page.astro -->
<aside transition:name="hero">
这个名字每页只能用一次。当 Astro 自己推断不出合适名字,或你想精确控制配对时,就手动指定。
保留状态 transition:persist
用 transition:persist 可以让元素「跨导航保留」,而不是被替换。比如下面这个视频,导航到另一页(只要那页也有同元素)会继续播,前进后退都算。
<video controls muted autoplay transition:persist>
<source src="https://example.com/video.mp4" type="video/mp4" />
</video>
它也能用在 Astro 岛屿(带 client: 指令的框架组件)上。如果该组件在下一页也存在,旧页面里「带着当前状态」的岛屿会继续显示,而不是被新页面的替换掉。
<Counter client:load transition:persist initialCount={5} />
配合 transition:persist-props,还能控制导航时岛屿的 props 是否保留。默认只保留状态、会用新 props 重渲染;再加 transition:persist-props 就连 props 也一起留着。
内置动画指令
Astro 内置几种动画来覆盖默认的 fade:
fade(默认):旧内容淡出、新内容淡入。initial:退出 Astro 的淡入淡出,用浏览器默认样式。slide:旧内容向左滑出、新内容从右滑入;后退时方向相反。none:关掉浏览器默认动画。用在<html>上可关掉全页默认淡入淡出。
组合使用就能完全掌控。比如在 <html> 上关掉默认淡入淡出,只让 <main> 滑动:
---
import CommonHead from "../components/CommonHead.astro";
---
<html transition:name="root" transition:animate="none">
<head>
<CommonHead />
</head>
<body>
<header>...</header>
<main transition:animate="slide">...</main>
</body>
</html>
客户端路由的控制
<ClientRouter /> 路由通过监听两件事来处理导航:点击 <a> 标签、以及前进/后退导航事件。几个常用控制点:
data-astro-reload:加在<a>或<form>上,强制整页刷新。data-astro-history="auto | push | replace":控制浏览器历史。navigate(href, options):任意客户端脚本或水合组件里都能调用,主动触发导航。
关闭某次客户端导航
有些链接你不想走客户端路由,加 data-astro-reload 即可,路由会忽略它、改成整页刷新:
<a href="/articles/emperor-penguins" data-astro-reload>
用代码触发导航
navigate() 来自 astro:transitions/client 模块,能在脚本里、或带客户端指令的水合框架组件里用。下面这个 Astro 组件,用户选了下拉项就自动跳走:
<script>
import { navigate } from "astro:transitions/client";
const select = document.querySelector("select");
if (select) {
select.onchange = (event) => {
if (event.target instanceof HTMLSelectElement) {
navigate(event.target.value);
}
};
}
</script>
<select>
<option value="/play">Play</option>
<option value="/blog">Blog</option>
<option value="/about">About</option>
<option value="/contact">Contact</option>
</select>
在 React 组件里也一样,只是要加 client:load 这样的指令:
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>
);
}
Tip
navigate()不会对传入的 URL 做净化。若 URL 来自用户输入,先校验再传,避免被?redirect=javascript:...这类值钻空子。考虑开启 Astro 的 CSP 配置来防 XSS。
回退策略 fallback
<ClientRouter /> 在支持 View Transitions 的浏览器(如 Chromium 系)里效果最好,对其他浏览器也有默认回退。给 <ClientRouter /> 加 fallback 属性可覆盖默认:
animate(默认,推荐):用自定义属性模拟视图过渡,再更新页面内容。swap:不做动画,旧页立刻被新页替换。none:完全不做动画过渡,在不支持的浏览器里走整页导航。
---
import { ClientRouter } from "astro:transitions";
---
<ClientRouter fallback="swap" />
脚本在过渡后的行为
开启视图过渡后,有些脚本在导航后不会再像整页刷新那样重跑。这点要留意:
- 打包后的模块脚本(Astro 默认)只会执行一次,之后即使新页面也有它,也会被忽略。
- 内联脚本可能在多次访问同一页时重跑,也可能在离开又回来时重跑。
想让脚本在导航周期的正确时机运行,可以把它包进事件监听器。比如把「汉堡菜单」的点击逻辑包进 astro:page-load(导航结束时触发):
document.addEventListener("astro:page-load", () => {
document.querySelector(".hamburger").addEventListener("click", () => {
document.querySelector(".nav-links").classList.toggle("expanded");
});
});
想让内联脚本每次过渡都重跑,加 data-astro-rerun:
<script is:inline data-astro-rerun>...</script>
生命周期事件
<ClientRouter /> 在导航期间会在 document 上触发一系列事件,给你在导航不同阶段插手:
astro:before-preparation—— 准备阶段开始,内容加载前。astro:after-preparation—— 准备阶段结束,新内容已加载并解析。astro:before-swap—— 新文档替换旧文档之前。astro:after-swap—— 新页替换旧页之后、DOM 渲染前。astro:page-load—— 导航结束,新页可见、阻塞资源加载完。
比如用 astro:after-swap 在 DOM 渲染前把滚动位置重置到左上角:
<script>
document.addEventListener("astro:after-swap", () =>
window.scrollTo({ left: 0, top: 0, behavior: "instant" }),
);
</script>
无障碍考虑
开启客户端路由和过渡动画都有无障碍挑战,Astro 尽量让默认就够友好。
路由播报
<ClientRouter /> 自带路由播报器,无需配置。它会给新页加一个 aria-live="assertive" 的元素,让读屏软件立刻播报。播报文字按优先级取:<title> → 第一个 <h1> → 页面路径。所以强烈建议每页都写 <title>。
尊重减少动画偏好
<ClientRouter /> 内含一段 CSS 媒体查询:一旦检测到 prefers-reduced-motion,就关掉所有视图过渡动画(含回退动画),浏览器直接换 DOM、不加动画。
小结
视图过渡靠 v7 的 <ClientRouter /> 开启,开启后 Astro 自动生成 SPA 式平滑导航。用 transition:name / animate / persist 精细控制,用 data-astro-reload 和 navigate() 控制路由,留意脚本重跑与无障碍。记得:旧 <ViewTransitions /> 已弃用,新项目一律写 <ClientRouter />。