如何利用 View Transitions API 实现原生级别的跨页面平滑过渡特效需要先看清适用场景和关键步骤,避免只记结论却忽略实际限制。
View Transitions API 不支持跨页面导航,仅适用于单页应用内同一文档的 DOM 更新;跨页跳转时旧文档卸载、新文档加载,导致过渡失效。
直接说结论:View Transitions API 本身不支持跨页面(即不同 HTML 文档)的过渡。它只在单页应用(SPA)内、同一文档上下文中的 DOM 更新时生效。如果你点击 <a href="/about"> 跳转到新页面,浏览器会卸载当前文档、加载新文档,此时 document.startViewTransition() 已无意义——旧 DOM 消失,新 DOM 尚未初始化。
常见错误现象是调用 startViewTransition() 后毫无动画,控制台也无报错;或者只看到部分元素闪动,但主体内容仍是硬切换。这是因为过渡被调用在了导航触发前,而实际 DOM 替换发生在导航完成之后,两者完全脱节。
可行路径只有两种:
history.pushState() + 手动更新 DOM),再配合 document.startViewTransition()
navigation API + document.visibilityState + 自定义 CSS 过渡的组合方案(非 View Transitions)核心原则:过渡必须包裹“将要发生变更的 DOM 更新操作”,且该操作必须同步、可预测。不能包裹异步数据获取,也不能包裹路由跳转本身(router.push() 等)。
典型错误写法:startViewTransition(() => router.push('/detail')) —— 这只是触发跳转,没碰 DOM。
正确做法是把 DOM 更新逻辑显式抽出来,在路由变化后、渲染前调用过渡:
router.afterEach((to, from) => { document.startViewTransition(() => { // ✅ 此处必须是同步 DOM 操作 app.innerHTML = renderPage(to.name); // 或调用框架的强制重绘方法 });});
注意点:
await、setTimeout 或任何异步延迟,否则过渡立即结束,动画中断forceUpdate/nextTick 触发同步渲染startViewTransition() 返回一个 ViewTransition 对象,可监听 finished Promise,但仅用于状态清理,不可用于驱动逻辑CSS 层面依赖两个关键属性:view-transition-name(标记参与过渡的元素)和 transition-style(定义如何过渡)。但它们目前(Chrome 111+)仍属实验性特性,Firefox/Safari 完全不支持,且行为不稳定。
常见坑:
view-transition-name,但元素插入 DOM 前该属性未生效 → 过渡失败view-transition-name 元素在新旧视图中同时存在 → 浏览器可能随机匹配,导致错位动画transition-style: fade 在 Chrome 120+ 才稳定,旧版需手动写 @property --view-transition-new(...) 变量稳妥做法是降级为传统 CSS 动画:
/* 当 View Transitions 不可用时兜底 */@supports not (view-transition-name: --my-title) { .page-enter { animation: fadeIn 0.3s; }}
SSR 页面首次加载时,HTML 已含完整 DOM,前端 JS 运行后执行 SPA 导航,这时 startViewTransition() 才介入。但问题在于:服务端渲染的“旧”内容与客户端渲染的“新”内容,DOM 结构常不一致(比如 SSR 渲染了占位骨架,CSR 渲染了真实数据),导致 view-transition-name 匹配失败。
解决方案有限且需权衡:
startViewTransition()
最易被忽略的一点:CSS 中的 view-transition-old() 和 view-transition-new() 伪元素默认 z-index 极低,常被其他层遮挡。务必显式设置 z-index: 1000 或更高,并确认父容器没有 overflow: hidden 截断动画。