View Transitions API 与原生动画

共 20 题
📑 题目列表 20 题
#
★★★

1. View Transitions API(Chrome 111+)的单页应用路由动画,原生 SPA 过渡替代 framer-motion 的工程取舍?

View Transitions API(Chrome 111+)如何用于单页应用的路由动画?用它替代 framer-motion 做 SPA 过渡的工程取舍是什么?

  • startViewTransition 的机制与 SPA 路由包装方式
  • 与 framer-motion 等动画库的能力对比
  • 兼容性、降级与维护成本的工程取舍

SPA 集成方式:在路由切换时调用 document.startViewTransition(() => 更新 DOM),浏览器自动捕获新旧状态快照并生成默认交叉淡入淡出,无需手写动画状态机;框架适配器(如 React 的 startTransition 包装、Vue 的 useViewTransition)把导航封装在过渡内,实现"路由即过渡"。与 framer-motion 的取舍:View Transitions 免费获得浏览器级快照过渡(布局、裁剪、伪元素动画),代码量极小且与框架无关,适合"页面级进出场";framer-motion 提供精细的共享元素动画、手势、弹簧曲线与组件级编排,适合"元素级、需要精细控制的动效"。工程取舍要点:默认用 View Transitions 做页面过渡降低体积与复杂度,framer-motion 只留给真正需要编排的场景;不支持浏览器(Firefox/Safari 旧版)时 startViewTransition 优雅降级为无动画切换,过渡代码不污染常规渲染路径。

答题框架是"机制 + 对比 + 取舍结论":先讲 startViewTransition 快照捕获原理与 SPA 包装,再对比两者能力边界(页面级 vs 元素级),最后给出"默认原生、特殊场景动画库"的分层结论,体现工程判断。

// 路由切换时包裹原生过渡
document.startViewTransition(async () => {
  await navigateTo(newRoute); // 更新 DOM
});
#
★★★

2. @starting-style 与 transition-behavior: allow-discrete 实现 display 切换的入场/离场过渡,及与 View Transitions 的分工

@starting-style 与 transition-behavior: allow-discrete 如何实现 display 切换的入场/离场过渡?它们与 View Transitions 的分工是什么?

  • @starting-style 定义元素首次渲染的起始样式
  • allow-discrete 使离散属性(display、visibility)可过渡
  • 与 View Transitions 在"进入/离开"动画上的职责划分

传统上 display: none ↔ block 无法过渡,因为离散属性不可插值。transition-behavior: allow-discrete 让 display 在过渡期间保持两端值之一参与离散切换,配合 visibility 的可过渡性实现"离场时元素先动画后隐藏";@starting-style 则提供元素从 display: none 进入渲染时的起始样式,实现入场动画(如 opacity 从 0 到 1、transform 位移)。两者组合让"出现/消失"不再需要 JS 状态机或动画库。与 View Transitions 的分工:@starting-style 面向"单个元素的进入/离开",由元素自身样式驱动、与路由无关;View Transitions 面向"整页或区域的新旧快照过渡",适合路由与大型视图切换。实践上弹层、下拉、toast 的进出场用 starting-style + allow-discrete,页面切换用 View Transitions,二者互补。

本题考察两条新 CSS 过渡路径的定位:starting-style 管"进入的起始状态",allow-discrete 管"离散属性的可过渡",两者解决元素级进出场;View Transitions 解决视图级快照切换。能分别举例场景即回答完整。

.popover {
  opacity: 1;
  transition: opacity 0.2s, display 0.2s allow-discrete;
}
.popover[hidden] { opacity: 0; display: none; }
.popover { @starting-style { opacity: 0; } }
#
★★

3. View Transitions 的跨文档(MPA)与同文档(SPA)模式原理,浏览器如何生成过渡快照?

View Transitions 的跨文档(MPA)与同文档(SPA)两种模式的原理是什么?浏览器如何生成过渡快照?

  • 同文档 startViewTransition 的旧/新快照捕获流程
  • 跨文档 @view-transition 规则与导航协议的衔接
  • 快照生成机制(渲染层复制、伪元素树挂载)

同文档模式(SPA):调用 startViewTransition 后浏览器立即捕获当前帧为旧快照,执行更新回调修改 DOM,渲染完成后捕获新快照,随后将两组快照挂载到 ::view-transition 伪元素树中做交叉过渡,结束后清理伪元素树、恢复正常渲染。跨文档模式(MPA):目标页面声明 @view-transition { navigation: auto; },浏览器在导航提交后、新页面首次渲染前捕获旧页面快照,新页面渲染后捕获新快照,同样用伪元素树过渡——整个过程由浏览器接管页面生命周期,源页面无需 JS。快照本质是渲染层的位图/纹理捕获(对含 view-transition-name 的元素按名字分组,其余合成整页层),因此动画是"截图间过渡"而非真实 DOM 动画,这也解释了为何过渡期间交互受限、为何大元素会被裁剪。

本题考原理层认知:两种模式的区别在"谁触发、何时捕获"(JS 回调 vs 导航生命周期),共性是"新旧快照 + 伪元素树过渡"。答出快照是渲染层捕获而非 DOM 动画,即超出背题水平的深度。

#
★★

4. Cross-document View Transitions 实现 MPA 间过渡,与 Astro / Qwik 等多页框架的协同

Cross-document View Transitions 如何实现 MPA 站点页面间过渡?它与 Astro、Qwik 等多页框架如何协同?

  • MPA 过渡的声明方式(@view-transition 规则)
  • 与框架导航、脚本执行的时序关系
  • 共享元素(view-transition-name)在多页间的匹配

协同方式:多页框架的每个页面是独立 HTML,在每页 CSS 中声明 @view-transition { navigation: auto; } 即可开启跨文档过渡,浏览器在页面间导航时自动执行快照过渡,框架无需任何改动——这是 MPA 过渡的天然优势。协同要点:同名 view-transition-name 的元素(如站点 logo、文章封面)在新旧页面都存在时会被浏览器配对做共享元素位移动画,实现"元素跨页连续"的观感;脚本时序上,新页面脚本仍在正常生命周期执行(过渡不阻塞),注意图片未加载完时新快照可能不完整,需配合资源加载处理;降级上,不支持时导航直接切换,声明规则不影响功能。Astro/Qwik 等框架还可在 View Transitions 之上叠加岛架构的局部更新,实现"MPA 骨架 + SPA 手感"。

答题核心是"MPA 过渡零 JS 接入":声明式规则开启,同名元素自动配对,框架只需保证每页 CSS 声明。答出与岛架构(Islands)叠加的渐进增强思路是加分项。

/* 每页 CSS 中开启跨文档过渡 */
@view-transition { navigation: auto; }
.logo { view-transition-name: site-logo; }
#
★★

5. 如何为 View Transitions 指定过渡元素(view-transition-name)并处理异步内容(图片加载)?

如何为 View Transitions 指定参与过渡的元素?异步内容(如图片加载)导致快照不完整时如何处理?

  • view-transition-name 的指定规则与唯一性要求
  • 命名元素进入独立组捕获、其余合成整页层的行为
  • 异步资源加载与快照时机的关系及解决方案

指定方式:给元素设置 CSS 属性 view-transition-name: 自定义名,该元素就会被独立捕获并放入同名的 ::view-transition-group 中参与过渡;命名必须是页面内唯一值(重复会报错并使过渡无效),未命名的内容被合成进默认的根快照组。命名后元素隐式获得 contain: layout/paint 等隔离属性,独立捕获使新旧页同名元素可做位移/缩放动画。异步内容处理:快照在新状态渲染完成时捕获,此时图片可能尚未加载完成导致新快照空白/模糊;解法是在更新回调中先等待资源就绪(await 图片 decode() 或 Promise.all 预加载关键图),或利用 transitionend/finished 事件监听再触发资源加载、用 loading="lazy" 之外的预加载策略保证首帧完整;字体同理需等待 document.fonts.ready。原则是"先等资源、再让浏览器捕获"。

答题要点分两块:命名规则(唯一性、独立分组、隐式 contain)与异步处理(快照时机在渲染后、需先 await 资源就绪)。能答出 decode()/fonts.ready 等待手段即证明实操深度。

.product-cover { view-transition-name: cover; }
await document.startViewTransition(async () => {
  await Promise.all(imgs.map((i) => i.decode())); // 等图片解码完成再更新 DOM
  updateDOM();
}).finished;
#
★★

6. View Transitions 与 FLIP 动画方案的对比,适用场景、性能与降级策略?

View Transitions 与 FLIP 动画方案相比有何异同?各自的适用场景、性能特点与降级策略是什么?

  • FLIP 的原理(First/Last/Invert/Play)与实现成本
  • View Transitions 快照机制的对比优势
  • 两者在不同场景的选择与降级

FLIP 是手写动画范式:记录元素变化前(First)与变化后(Last)的位置,用 transform 反向补偿(Invert)再播放(Play),把昂贵布局属性动画转为合成器处理的 transform 动画;它需要 JS 测量、对每个元素手动实现,并自行处理 First/Last/Invert/Play 四步。View Transitions 由浏览器自动完成快照捕获与插值,开发者只需 startViewTransition 加 CSS 自定义,代码量大幅减少,且快照过渡天然支持跨页匹配。性能对比:FLIP 只对 transform/opacity 做合成动画,可控性强;View Transitions 的快照是渲染层拷贝,过渡帧率由浏览器保证,但快照占内存、大元素截断风险。适用场景:单个或少量元素的布局动画选 FLIP 或过渡属性;整页/大区域切换、跨页共享元素选 View Transitions。降级:FLIP 降级为无动画直接跳变;View Transitions 在不支持浏览器下自动降级为普通更新,两种方案都应在设计上保证"无动画时功能完整"。

本题对比题:先讲清 FLIP 四步原理与手工成本,再对比 View Transitions 的自动化快照,最后落到"元素级用 FLIP/过渡、页面级用 View Transitions、均保证无动画可降级"。结论清晰是得分关键。

#
★★

7. View Transitions 与 SPA 路由框架(React/Vue)的集成,如何用 navigate 事件或框架适配器触发过渡?

View Transitions 与 React/Vue 等 SPA 路由框架如何集成?navigate 事件与框架适配器分别如何触发过渡?

  • 路由更新包进 startViewTransition 的集成模式
  • Navigation API 的 navigate 事件拦截方式
  • 框架适配器与已有过渡库的兼容策略

两种集成路径:一是手动包装——把"更新路由状态并渲染"的调用放进 startViewTransition 回调(React 中包裹 setState 或 startTransition,Vue 中包裹路由 push),路由库本身无需感知过渡;二是用 Navigation API——监听 window 的 navigate 事件并 preventDefault,在回调里手动执行 startViewTransition 完成导航,获得对前进/后退等各类导航的统一拦截点。框架适配器(如 react-transition-progress、vue 的 useViewTransition 封装、SvelteKit 内置 viewTransition 选项)把上述模式固化为声明式配置,少写样板。集成要点:过渡回调里必须是同步更新 DOM 或返回 Promise,不能把异步数据请求直接放进回调(否则快照延迟);与 framer-motion 等库共存时让动画库处理元素级动效、View Transitions 处理页面级,避免两者争抢同一元素的过渡。

答题主线是"过渡包裹导航更新":手动模式讲 startViewTransition 包路由更新,事件模式讲 navigate 事件拦截,适配器是前两者的封装。答出"回调内应同步更新或返回 Promise、数据请求放外面"是工程细节得分点。

navigation.addEventListener("navigate", (e) => {
  if (!shouldTransition(e.destination.url)) return;
  e.intercept({ handler: async () => {
    await document.startViewTransition(async () => {
      await router.navigate(e.destination.url);
    }).finished;
  }});
});
#
★★

8. View Transitions 的降级策略,不支持浏览器(Firefox/Safari 旧版)时的回退动画如何优雅实现?

在不支持 View Transitions 的浏览器(如 Firefox、旧版 Safari)中,回退动画应如何优雅实现?

  • 能力检测 document.startViewTransition 的方式
  • 降级路径的分层设计(无动画直切 / 简易动画回退)
  • 过渡增强(progressive enhancement)的工程原则

降级分三层设计:第一层是能力检测——typeof document.startViewTransition === "function" 判断可用性,不可用时直接执行常规更新,页面切换无动画但功能完整;第二层是可选回退动画——检测失败时用 CSS 动画或 transition 做简易淡入(如给新内容加 fade-in 类),让低版本浏览器也有基本观感,但绝不阻塞交互;第三层是避免副作用依赖——不把业务逻辑挂在过渡生命周期上(如依赖 finished 完成后才执行的状态),保证无过渡时逻辑等价。实现上可封装一个小适配器:有 API 走 startViewTransition,无 API 走直切或 CSS 回退,上层调用方无感。原则是"过渡是增强不是依赖":动画失败不能让页面功能受损,这是渐进增强的底线。

本题考降级工程观:能力检测 + 分层次回退(无动画直切为底线、CSS 简易动画为可选增强)+ 业务逻辑不依赖过渡生命周期。答出"动画即增强、功能不依赖动画"即为优秀。

const supportsVT = () => typeof document.startViewTransition === "function";
function transitionUpdate(update) {
  if (supportsVT()) return document.startViewTransition(update);
  update(); // 不支持:直接更新,无动画降级
}
#
★★

9. ViewTransition 的生命周期 Promise(updateCallbackDone/ready/finished)与 transition 事件在动画编排与失败处理的工程应用?

ViewTransition 的生命周期 Promise(updateCallbackDone、ready、finished)与 transition 事件在动画编排与失败处理上有何工程应用?

  • 三个 Promise 各自代表的阶段与完成时机
  • 用 ready/finished 编排动画与资源释放
  • 更新失败(回调抛错)时的状态处理

startViewTransition 返回的 ViewTransition 暴露三个阶段:updateCallbackDone 在更新回调完成后 resolve(不论动画成败),ready 在新快照捕获、过渡伪元素树准备就绪后 resolve(此后可安全操作动画参数,如修改 ::view-transition 的动画时长),finished 在过渡完全结束后 resolve(用于清理临时状态、触发后续逻辑)。工程应用:在 ready 后动态调整伪元素动画属性实现可编程编排;在 finished 里做后置动作(移除临时样式、发送埋点);用 Promise.race 给过渡加超时兜底,防止个别浏览器卡死。失败处理:更新回调抛错时 updateCallbackDone reject、过渡不执行且 DOM 已部分更新,需用 try/finally 恢复一致性(如回滚 DOM 状态);finished 在过渡被取消时也 resolve,不可当作"成功"信号。原则是"updateCallbackDone 管更新成败、finished 管动画收尾、异常路径显式处理"。

本题考 API 细节:三个 Promise 的时机差异是核心(回调完成 ≠ 动画完成 ≠ 更新成功),围绕它们做编排、超时与失败恢复。答出"finished resolve 不代表成功、回调抛错需回滚 DOM"即显深度。

const vt = document.startViewTransition(() => updateDOM());
await vt.ready; // 新快照就绪,可调整动画参数
await vt.finished; // 过渡结束,清理状态
vt.updateCallbackDone.catch(() => rollbackDOM()); // 更新失败回滚
#
★★

10. scroll-driven animations,animation-timeline 的 scroll()/view() 实现滚动进度动画与合成性能边界

scroll-driven animations 中 animation-timeline 的 scroll() 与 view() 如何实现滚动进度动画?其合成性能边界在哪里?

  • scroll()/view() 时间线的语义与参数(scroller、axis、block/inline)
  • animation-range 定义动画生效的滚动区间
  • 动画在合成器线程执行的性能优势与边界

scroll-driven animations 用 animation-timeline 替代默认的时间线:scroll() 把动画进度绑定到滚动容器的滚动进度(可指定 scroller 与 axis),元素随页面滚动位置播放动画;view() 把进度绑定到元素自身进入/离开视口的进度(基于元素与视口的交集),配合 animation-range 限定动画起止区间(如"元素进入视口到完全可见")。性能:这类动画由合成器线程直接消费滚动进度,无需主线程逐帧计算,滚动过程不掉帧;边界在于:只有 transform/opacity 等可合成属性才能全程脱离主线程,改布局属性(width、height)会强制主线程参与;时间线依赖的滚动容器需可滚动(overflow 设置),嵌套滚动容器需显式指定 scroller;与 scroll-snap、惯性滚动配合时进度可能跳变。工程上优先用可合成属性驱动滚动动画,并设置 animation-timeline 的 fallback(旧浏览器无动画或静态样式)。

答题要点:scroll()/view() 的语义差异(滚动进度 vs 视口可见度)、animation-range 控制区间、合成器线程收益及其边界(仅可合成属性)。能答出 fallback 与滚动容器约束即完整。

.progress-bar { animation: grow linear; animation-timeline: scroll(nearest block); }
.card { animation: reveal linear; animation-timeline: view(block); animation-range: entry 0% entry 100%; }
#
★★

11. view-transition-name 隐式施加 contain 对元素布局与快照裁剪的影响(大元素截断排查)

view-transition-name 隐式施加的 contain 属性对元素布局与快照裁剪有何影响?大元素被截断时应如何排查?

  • view-transition-name 隐式 contain 的隔离语义
  • 快照裁剪与整页合成边界的成因
  • 大元素截断的排查思路与解法

设置 view-transition-name 的元素会被隐式施加 contain: layout style paint(及 size 视情况),使其成为独立的渲染隔离单元,保证快照捕获不受外部影响;副作用是该元素内部的布局变化不再影响外部,且元素可能产生自己的包含块与层叠上下文。快照裁剪的成因:命名元素的快照默认限制在其自身的边界盒内,超出部分(阴影、溢出内容、transform 放大的子元素、fixed 定位弹层)不会出现在快照中,表现就是"动画时元素被截断";整页默认过渡同样受视口与根组尺寸约束。排查思路:先确认截断元素是否被命名(取消命名验证是否为默认根组问题);再检查元素溢出内容与阴影是否在边界盒内,必要时把阴影/弹层也命名或调整边界;大元素(如整屏图表)可考虑不命名走整页过渡,或拆分命名多个子元素;同时注意 contain: paint 裁剪与 transform 缩放的组合行为。解法原则是"命名的边界即快照的边界"。

本题考隐式 contain 的连锁影响:命名带来隔离与裁剪,快照边界 = 元素边界盒。排查要按"是否命名 → 内容是否越界 → 是否拆分命名"的路径走,答出阴影/溢出与 transform 场景即显经验。

#

12. SSR/CSR 下 View Transitions 的兼容性处理与无障碍(prefers-reduced-motion)适配?

SSR 与 CSR 场景下 View Transitions 的兼容性如何处理?prefers-reduced-motion 等无障碍偏好如何适配?

  • SSR 下能力检测与注水(hydration)时序
  • 动画缩短/关闭的 reduced-motion 适配
  • 过渡期间可访问性状态(焦点、ARIA)的保持

兼容性处理:能力检测(document.startViewTransition 是否存在)是运行时行为,SSR 下服务端不执行、客户端注水后再启用,避免服务端渲染与客户端行为不一致;同时用媒体查询或 JS matchMedia 判断 reduced-motion 偏好,偏好开启时跳过过渡直接更新,或缩短动画时长(如 0.1s 淡入而非位移)。无障碍适配要点:prefers-reduced-motion: reduce 是硬性要求,动画必须可关闭(CSS 侧可用 @media 覆盖动画时长/取消位移);过渡期间保持焦点正确——新页面获得焦点元素应合理(通常聚焦文档或路由锚点),避免过渡结束后焦点丢失;对屏幕阅读器,页面切换用真实导航语义(MPA 的 title 更新、SPA 的 aria-live 提示),快照动画是纯视觉层,不应干扰 AT 的文档流。原则是"动画是视觉增强,reduced-motion 与焦点管理优先于动效表现"。

答题两点:SSR 兼容靠运行时能力检测与注水后启用;无障碍靠 reduced-motion 关闭/缩短动画与过渡后焦点管理。答出"视觉动画不干扰辅助技术文档流"的边界即完整。

@media (prefers-reduced-motion: reduce) {
  ::view-transition-group(*), ::view-transition-old(*), ::view-transition-new(*) {
    animation-duration: 0.01ms !important;
  }
}
#

13. 过渡与懒加载内容,图片/字体加载未完成时快照不同步的问题,如何用 transitionend/等待资源解决?

懒加载的图片或字体未加载完成时,View Transitions 快照会不同步,应如何用 transitionend 或资源等待解决?

  • 快照捕获时机与资源加载的竞态
  • 等待资源就绪的 API(img.decode、document.fonts.ready、Promise.all)
  • 事件监听与重试/兜底策略

问题本质是竞态:新快照在 DOM 更新后立即捕获,而懒加载图片可能尚未加载、webfont 未就绪,导致新快照出现空白图或字体回退的错乱画面。解法在"让资源先于快照就绪":更新回调内先 await 关键图片的 decode()(确保解码完成且渲染可用),字体用 document.fonts.ready 或 font-display: swap 缓解回退闪烁;对非关键资源可标记为延迟加载但排除出首帧快照区域。transitionend(或 transitionstart/end 事件)用于另一个方向:监听过渡伪元素动画的结束做后置处理(如清理 loading 状态、再触发剩余懒加载),把"先保证首帧、再补齐细节"分层。兜底策略:资源等待设置超时,超时后照常完成过渡(视觉轻微降级但不卡导航);个别失败资源用占位图兜底。原则是"快照要的是首帧完整,不是全资源就绪"。

本题考竞态处理:快照时机 vs 资源时机。解法是"更新前 await 关键资源(decode/fonts.ready)、过渡后用 transitionend 触发后置加载、超时兜底"。答出"首帧完整优先"的分层思路即到位。

await document.startViewTransition(async () => {
  const imgs = [...document.images];
  await Promise.allSettled(imgs.map((i) => i.decode()));
  await document.fonts.ready;
  updateDOM();
}).finished;
#

14. 与 FLIP 动画的对比,性能与适用场景?

View Transitions 与 FLIP 动画在性能与适用场景上有哪些差异?各自在什么场景下更合适?

  • FLIP 的 transform 合成动画与测量成本
  • View Transitions 快照机制的渲染开销特征
  • 场景化选择:元素级 vs 视图级、频繁触发 vs 单次切换

性能对比:FLIP 先测量 First/Last 布局再反向位移播放,测量阶段强制布局(可能 layout thrash,需批量读取规避),播放阶段只动 transform/opacity 由合成器执行,适合高频、少量元素的布局变化;View Transitions 的快照捕获是渲染层复制,首帧开销在捕获与伪元素合成,动画过程同样走合成器,但快照占内存、过渡期间交互受限,适合低频、大区域的视图切换。适用场景:列表重排、拖拽排序、展开收起等"同一页面少量元素位置变化"用 FLIP 或纯 transition;路由切换、跨页共享元素、整块内容替换用 View Transitions。工程取舍还看维护成本:FLIP 手写逻辑多、要处理反向与边界,View Transitions 声明式、代码少但依赖浏览器支持;两者也可共存——页面级用 View Transitions,页面内元素级用 FLIP/transition。

对比题要落到"场景-成本"结论:FLIP 测量贵但可控、适合元素级高频;View Transitions 快照贵但声明式、适合视图级低频。最后给共存方案更显工程全局观。

#

15. 无障碍与减少动画偏好(prefers-reduced-motion)?

View Transitions 的无障碍适配中,prefers-reduced-motion 偏好应如何处理?还有哪些无障碍注意点?

  • prefers-reduced-motion 的检测与动画关闭策略
  • 过渡期间与结束后的焦点管理
  • 视觉动画与辅助技术信息呈现的分工

处理 reduced-motion:用 CSS @media (prefers-reduced-motion: reduce) 覆盖伪元素动画时长(设 0.01ms 或无位移)或整体禁用过渡,JS 侧用 matchMedia("(prefers-reduced-motion: reduce)") 在调用 startViewTransition 前直接跳过;系统偏好开启时任何动画库都应遵守,这是 WCAG 2.3.3 动画交互的基本要求。其他注意点:焦点管理——过渡结束后焦点应落在合理位置(新路由的标题/内容锚点),避免焦点丢失或停留在已卸载元素;过渡期间快速导航会被打断,需保证打断后状态一致;快照动画是视觉呈现,页面切换本身的语义(title 更新、文档结构变化、aria-live 提示)仍由正常导航提供,不因动画增强而改变。原则是"偏好优先、焦点兜底、语义不受动画影响"。

答题要点:reduced-motion 双通道关闭(CSS 覆盖 + JS 跳过)、过渡后焦点管理、动画不改变语义信息。能引用 WCAG 与"偏好优先于动效"即为完整回答。

#

16. View Transitions 的兼容与降级,不支持浏览器的回退?

不支持 View Transitions 的浏览器应如何回退?兼容性与降级策略有哪些?

  • 支持矩阵(Chrome 111+ 同文档、Chrome 126+ 跨文档、Safari 18+)
  • 能力检测与 polyfill 的可行性边界
  • 无动画直切的分层降级设计

支持现状:同文档过渡 Chrome 111+、Safari 18+、Firefox 144+ 已支持;跨文档过渡 Chrome 126+、Safari 18+(Firefox 尚未支持)。回退策略按层设计:第一层能力检测——typeof document.startViewTransition !== "undefined" 才调用,不支持时直接执行 DOM 更新,无动画但功能完整;第二层可选增强——不支持时用 CSS transition/animation 做简易淡入(检测失败分支给根元素加过渡类),追求观感不追求一致;第三层约定——不把业务逻辑(状态提交、埋点、焦点跳转)放进过渡回调或 finished 依赖中,保证无过渡路径与有过渡路径行为一致。polyfill 的边界:社区 polyfill 可模拟同文档模式(内部用 CSS 动画实现),但无法还原真正的渲染层快照(跨文档模式无 polyfill),性能与效果有折扣,小型站点可选用、复杂页面建议直接降级。原则是"支持则增强、不支持则无损直切"。

本题考"渐进增强"落地:先讲支持矩阵现状,再讲三层降级(能力检测直切、CSS 简易回退、逻辑不依赖过渡),最后评价 polyfill 的真实边界。答出"跨文档无 polyfill"即显信息准确性。

#

17. View Transitions 与路由框架的集成?

View Transitions 与主流路由框架的集成方式有哪些?集成时的常见坑是什么?

  • React/Vue/Svelte 各自的集成模式与官方支持
  • 更新回调与框架渲染时序的配合
  • 常见坑:异步更新、数据加载、重复触发

集成模式:React 中把路由状态更新包进 startViewTransition(配合 startTransition 让过渡与并发渲染协调),或用现成的 useViewTransition 封装 hook;Vue 3.4+ 的 已内建 view-transition 模式(transition name 自动生成伪元素命名);SvelteKit 提供内置的 viewTransition 选项直接作用于导航;Angular 通过 NavigationStart 事件配合。共同点都是"框架负责更新 DOM,过渡负责包装更新"。常见坑:一是回调内做异步数据请求——回调应尽快完成,数据加载放外部、等待后再更新,否则新快照迟迟不产生;二是重复触发——连续导航时旧过渡未结束又开新过渡,需在回调里做防抖或等待 finished;三是 SSR 水合期调用 API——document 不存在或水合未完成时报错,需挂载后调用;四是动画类与框架类并存时样式冲突(如 framer-motion 也操作 transform)。处理原则是"包装渲染而非包装业务、数据先行、导航串行化"。

本题考集成实操:各框架接入点 + 常见坑。坑的枚举要具体(异步回调、重复触发、水合期调用、动画库冲突),能讲出"包装渲染不包装业务"即抓住本质。

#

18. View Transitions 的伪元素结构(::view-transition、::view-transition-group/new/old)与动画参数(duration/easing)的自定义方式?

View Transitions 的伪元素结构(::view-transition、::view-transition-group、::view-transition-new/old)是怎样的?动画参数(duration/easing)如何自定义?

  • 伪元素树的层级与命名分组机制
  • new/old 快照伪元素与默认动画
  • 用 CSS 覆盖 duration/easing 与自定义 keyframes

过渡期间浏览器挂载伪元素树:根 ::view-transition 包含每个命名组一个 ::view-transition-group(name)(未命名内容归入根组),组内是 ::view-transition-image-pair 与 ::view-transition-old(name)、::view-transition-new(name) 两个快照伪元素——old 承载旧快照、new 承载新快照,默认动画是两者交叉淡入淡出(根组)或位置/尺寸插值(命名组)。自定义方式:直接给伪元素写 CSS——覆盖 animation-duration/animation-timing-function,或替换整个 keyframes(如给 new 加位移与缩放、给 old 加模糊淡出);不同命名组可分别设置动画(如 header 与 content 用不同曲线),实现分组编排。工程注意:伪元素动画只在过渡窗口期存在,样式应在全局 CSS 中声明;动画期间不要改 ::view-transition 之外的结构,避免快照与新状态错位。掌握伪元素树即可把"浏览器默认过渡"升级为"可编程的页面转场"。

本题考机制细节:伪元素树的父子层级(root→group→image-pair→old/new)与命名分组,自定义靠覆盖伪元素动画属性或替换 keyframes。答出按命名组分别定制动画即显实操深度。

::view-transition-old(root) { animation: fade-out 0.25s ease; }
::view-transition-new(root) { animation: fade-in 0.35s cubic-bezier(0.4, 0, 0.2, 1); }
@keyframes fade-in { from { opacity: 0; transform: translateY(8px); } to { opacity: 1; } }
#

19. View Transitions 性能调优,减少 layout shift 与 paint 的最佳实践

View Transitions 的性能调优有哪些要点?如何减少 layout shift 与 paint 开销?

  • 快照捕获阶段的布局与绘制成本来源
  • 命名元素与合成层对性能的影响
  • 资源加载、动画属性与降级的性能策略

调优围绕"捕获成本与动画成本"两端:捕获阶段浏览器要对新旧状态做布局与绘制(快照即渲染层拷贝),因此更新回调内的 DOM 变更应尽量小——避免整页重建、复用已有节点、批量更新减少布局次数;大区域或复杂元素(大图、SVG、Canvas)捕获开销高,能不命名就不命名,确需动画时用较小区域或拆分命名。动画阶段:伪元素动画只建议使用 transform/opacity 等可合成属性,避免在伪元素动画中改 layout 属性造成每帧重排;减少 layout shift 的根本是给图片/媒体预留尺寸(aspect-ratio、width/height),过渡前后布局差异大时快照位移也大。其他实践:关键资源预加载(preload 图片与字体)保证新快照完整、减少首帧闪烁;配合 content-visibility 隔离非关键内容;低端设备可依据内存/偏好降级为无动画。原则是"捕获前结构稳定、动画只动合成属性、资源先就绪"。

答题框架是"捕获成本(DOM 变更越小越好)+ 动画成本(只动合成属性)+ 资源与降级"。能答出"预置媒体尺寸减少 shift、非命名区域不参与独立快照"即为有实践经验的回答。

#

20. startViewTransition 中断编排,新过渡打断旧过渡时的快照替换时序与闪烁规避

startViewTransition 被新过渡打断时,快照替换的时序是怎样的?如何规避闪烁?

  • 连续导航时过渡的取消与替换机制
  • 新快照与旧快照的时序竞态
  • 防抖、取消与等待策略避免闪烁

打断时序:新的 startViewTransition 调用会取消正在进行的过渡——旧过渡的伪元素树被移除、其 finished 提前 resolve(不代表成功),新过渡立即捕获当前帧为新旧快照开始动画;若打断发生在旧过渡捕获后、动画播放中,新过渡的"旧快照"是打断瞬间的中间帧,可能产生闪烁或跳变。规避策略:一是导航串行化——同一路由层级的连续导航做防抖或队列,等待前一次 finished 后再触发下一次,避免高频打断(快速连点导航、多标签切换);二是打断感知——用 updateCallbackDone/finished 检测取消,清理半完成状态(如已改一半的 DOM 用快照恢复);三是资源等待前置——把图片等资源就绪放在 startViewTransition 之前,缩短每次过渡窗口,减少被打断的暴露面;四是闪烁兜底——新快照捕获前保持 DOM 稳定,若检测到 cancelled 状态可执行一次无动画更新保证最终态正确。原则是"过渡可打断但最终态必须正确"。

本题考边角工程:打断的时序(旧过渡被取消、新过渡以中间帧为旧快照)与三条规避路径(串行化、取消感知、窗口缩短)。答出"finished 提前 resolve 不代表成功"即体现 API 细节理解。