view transitions 在 spa 中需手动拦截导航并同步更新 dom,css 伪元素动画时长必须一致,共享元素需严格匹配 view-transition-name,且必须由用户手势触发。

document.startViewTransition() 必须包裹同步 DOM 更新
跨页面过渡不是指跳转到新 HTML 文件,而是单页应用(SPA)中模拟“页面切换”的视觉效果。浏览器不会为 location.href 或 <a href="/next"></a> 自动启用 View Transitions;你必须手动拦截导航、阻止默认行为,并在 document.startViewTransition() 回调中**同步完成 DOM 替换**。
常见错误是把异步操作塞进回调里:
- ❌
document.startViewTransition(() => fetch('/page').then(html => main.innerHTML = html))—— fetch 已异步,快照在 fetch 开始前就拍完了,新内容根本没被捕捉 - ✅ 正确做法:先确保 HTML 字符串或 DOM 片段已就绪,再进回调执行
replaceChildren()或innerHTML = ... - ⚠️ React/Vue 用户注意:
router.push()或setState()本身不触发视图同步更新,需配合flushSync(React)或await nextTick()(Vue)后再调用startViewTransition
::view-transition-old(root) 和 ::view-transition-new(root) 的样式必须配对且时长一致
这两个伪元素控制整页过渡的起止状态。浏览器靠它们生成两层快照并叠加动画,一旦配置失衡,就会出现闪白、卡顿或只显示新内容。
- ❌
::view-transition-old(root) { animation-duration: 0.2s; }与::view-transition-new(root) { animation-duration: 0.4s; }—— 旧层早退、新层晚到,中间空档直接暴露白屏 - ✅ 两者
animation-duration、animation-timing-function必须完全一致;推荐统一写在::view-transition上作为基础配置 - ⚠️ 禁止在快照前隐藏旧内容:
display: none、visibility: hidden、opacity: 0都会让旧快照为空;安全替代是opacity: 0.001或backdrop-filter: blur(1px)
view-transition-name 必须严格匹配才能启用共享元素动画
如果你希望标题“飞入”、图片“缩放”,而不是整页淡入淡出,就得靠 view-transition-name 显式声明哪些元素参与命名过渡。它不是 class,不是 data 属性,是独立 CSS 属性,匹配极其敏感。
- ✅ 正确:
<h1 style="view-transition-name: title">首页</h1>→ 新 DOM 中同逻辑位置也必须有style="view-transition-name: title" - ❌ 错误:
view-transition-name: title-${id}但前后id不同;或一个写"title",另一个写"Title"(大小写不一致) - ⚠️ 同一过渡帧内不能重复使用同一名称:两个
<div> 都设 <code>view-transition-name: card,浏览器无法区分对应关系,动画会错乱@view-transition 规则仅适用于跨文档导航(MPA),当前支持极有限
你可能看到文档提到
@view-transition可用于真正的多页应用(MPA)跳转,比如从/home到/about的 HTML 页面切换。但截至 2026 年 10 月,该语法仍处于实验阶段,仅 Chrome Canary 少数版本部分支持,且要求服务器响应头明确声明View-Transition: enabled,目前几乎不可用于生产。所以实际能稳定落地的,仍是 SPA 场景下的
document.startViewTransition()+ CSS 伪类组合。别指望给<a href="/next.html"></a>加个属性就自动动起来——那不是 View Transitions 的设计边界。最容易被忽略的一点:所有过渡都依赖用户手势触发(click、keydown 等)。如果在
setTimeout或组件挂载后自动调用startViewTransition,多数浏览器会静默拒绝,连报错都没有。











