首页 / Astro 教程 / 视图过渡 View Transitions(ClientRouter)

Astro 教程

视图过渡 View Transitions(ClientRouter)

本教程共 56 篇 · 第 32 篇 · 更新于 2026-08-07 · 约 10 分钟阅读

AstroAstro 教程视图过渡View TransitionsClientRouter客户端路由transition:persist群岛架构

本节目标:学会用 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 上触发一系列事件,给你在导航不同阶段插手:

  1. astro:before-preparation —— 准备阶段开始,内容加载前。
  2. astro:after-preparation —— 准备阶段结束,新内容已加载并解析。
  3. astro:before-swap —— 新文档替换旧文档之前。
  4. astro:after-swap —— 新页替换旧页之后、DOM 渲染前。
  5. 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-reloadnavigate() 控制路由,留意脚本重跑与无障碍。记得:旧 <ViewTransitions /> 已弃用,新项目一律写 <ClientRouter />