案例研究:Google 如何使用视图转换为其新的 AI 模式打造互联体验

发布时间:2025 年 8 月 28 日

Google 搜索是全球覆盖面最广的产品之一,因此用户体验方面的变化可能会影响数十亿用户。我们一直梦想着能打造出更现代、更像应用的网络体验。在开始开发 AI 模式时,我们希望为用户打造一种体验,让他们能够从标准搜索无缝过渡到 AI 模式。当我们听说跨文档视图过渡时,就知道它与该功能是天作之合。本案例研究将分享我们在推出 AI 模式的同时添加过渡功能时获得的经验。

在 Google 搜索中进行搜索的录制内容,从搜索结果切换到 AI 模式。过渡效果使用了视图过渡。

对于原生浏览器工具而言,跨文档视图过渡是一项颠覆性功能,我们很高兴看到它将如何塑造未来的 Web。

Browser Support

  • Chrome: 126.
  • Edge: 126.
  • Firefox: not supported.
  • Safari: 18.2.

Source

改变现状

Google 搜索对浏览器支持有严格而保守的要求。一般来说,使用小范围开放使用的功能是禁止的。对于跨文档视图过渡,我们发现 polyfill 不可行,主要原因是缺少像素快照 API,并且克隆整个视口存在严重的性能问题。因此,将该功能用作渐进式增强功能是与 AI 模式一起发布的最佳方式。由于视图转换创建的动画不会直接影响网站的功能,因此对于不受支持的流量,这些动画会被停用,而这已经是当前没有转换动画的正式版状态。

我们首先面向内部用户测试了这种渐进式增强策略。这让我们获得了早期反馈,而且反馈非常积极。收到的反馈还揭示了一些 bug,包括性能问题以及与其他功能(例如重叠的堆叠上下文)的意外互动。

我们发现这种策略非常成功,相信未来我们会尝试将这种策略应用于其他新的浏览器功能。

我们遇到的困难以及如何解决这些困难

延迟时间、渲染阻塞和看门狗计时器

总体而言,对于 99% 的使用情形,尤其是对于现代硬件,MPA 视图转换带来的额外延迟可以忽略不计。不过,Google 搜索对延迟的要求非常高,我们致力于打造在所有设备上都能顺畅运行的用户体验。对我们来说,即使是几毫秒的延迟也很重要,因此我们必须投入精力研究如何正确实现跨文档视图过渡,同时又不损害任何人的用户体验。

渲染阻塞是一种与跨文档视图过渡搭配使用的出色技术。传入文档的伪元素快照只能显示已呈现的内容。因此,若要为传入文档中的内容添加动画效果,您需要渲染块,直到要添加动画效果的目标元素已渲染完毕。为此,请在 HTMLLinkElement 上使用blocking 属性。渲染阻塞也有其缺点,因为等待位于传入文档的 DOM 树末尾的元素可能会对延迟时间产生重大影响。我们必须相应地平衡这种权衡,并且仅在网页生命周期中极早渲染的元素上渲染阻塞。

<!-- Link tag in the <head> of the incoming document -->
<link blocking="render" href="#target-id" rel="expect">
<!-- Element you want to animate in the <body> of the incoming document -->
<div id="target-id">
  some content
</div>

在某些情况下,即使您准确指定了要屏蔽的元素,也无法达到预期效果。即使在 DOM 树开头的元素上阻止渲染,某些设备或连接仍会看到延迟增加。为了处理这些情况,我们编写了一个看门狗计时器脚本,用于在经过一定时间后移除 HTMLLinkElement,以强制取消阻止对传入文档的渲染。

一种简单的方法如下:

function unblockRendering() {
  const renderBlockingElements = document.querySelectorAll(
    'link[blocking=render]',
  );
  for (const element of renderBlockingElements) {
    element.remove();
  }
}

const timeToUnblockRendering = t - performance.now();

if (timeToUnblockRendering > 0) {
  setTimeout(unblockRendering, timeToUnblockRendering);
} else {
  unblockRendering();
}

覆盖范围限制

我们遇到的另一个问题是,跨文档视图过渡 at-rule navigation: auto 在文档中以全局级别发生。目前还没有内置方法可以将启用跨文档视图转换的范围限定为仅限特定点击目标。由于这是一项重大变更,我们无法在 Google 搜索中针对所有导航操作启用跨文档视图过渡效果。我们需要一种方法来根据用户正在互动的功能动态启用或停用跨文档视图过渡。在本例中,我们仅针对模式更改(从 AI 模式更改为其他模式,以及从其他模式更改为 AI 模式)启用了这些动画。我们通过以编程方式更新导航 at-rule 来实现此目的,具体取决于点击或点按的目标。

切换视图过渡 @ 规则的方法如下:

let viewTransitionAtRule: HTMLElement | undefined;
const DISABLED_VIEW_TRANSITION = '@view-transition{navigation:none;}';
const ENABLED_VIEW_TRANSITION = '@view-transition{navigation:auto;}';

function getVtAtRule(): HTMLElement {
  if (!viewTransitionAtRule) {
    viewTransitionAtRule = document.createElement('style');
    document.head.append(viewTransitionAtRule);
  }
  return viewTransitionAtRule;
}

function disableVt() {
  getVtAtRule().textContent = DISABLED_VIEW_TRANSITION;
}

function enableVt() {
  getVtAtRule().textContent = ENABLED_VIEW_TRANSITION;
}

卡顿和合成动画

视图过渡伪元素上的一些自动生成的动画导致旧设备上出现丢帧,从而影响了我们想要为用户提供的流畅体验。为了提高动画的性能,我们使用可在合成器上运行的动画技术重写了这些动画。我们通过检查关键帧来获取前后快照伪元素的尺寸,并使用矩阵数学相应地重写关键帧,从而实现了这一目标。以下示例展示了如何获取每个视图过渡伪元素的动画:

const pseudoElement = `::view-transition-group(${name})`;
const animation = document
  .getAnimations()
  .find(
    (animation) =>
      (animation.effect as KeyframeEffect)?.pseudoElement === pseudoElement,
  );

如需详细了解如何编写高性能的视图过渡关键帧,请参阅应用视图过渡:处理包含块的快照

其他注意事项

一个比较突出的问题是,使用 view-transition-name CSS 属性标记元素会影响堆叠上下文(查看过渡规范:第 2.1.1 节)。这导致了多个 bug,需要修改容器元素的 z-index

另一点需要注意的是,您可能不想默认向元素添加 view-transition-name 值。有很多人在从事 Google 搜索方面的工作。为了防止我们团队在元素上设置的 view-transition-name 值与来自其他团队的人员可能使用的值发生冲突,我们利用视图过渡类型,仅在特定视图过渡类型处于活动状态时有条件地添加 view-transition-name 属性。

以下是一个 CSS 示例,用于仅在 ai-mode 的视图过渡类型处于活动状态时,向元素添加 the-elementview-transition-name

html:active-view-transition-type(ai-mode) {
  #target {
    view-transition-name: the-element;
  }
}

为所有视图过渡设置好这些 CSS 规则后,您便可以在 pageswappagereveal 事件期间动态更改任何导航的当前视图过渡类型。

pageswap 事件期间将视图过渡类型更新为 ai-mode 的示例。

function updateViewTransitionTypes(
  event: ViewTransitionEvent,
  types: string[],
): void {
  event.viewTransition.types.clear();
  for (const type of types) {
    event.viewTransition.types.add(type);
  }
}

window.addEventListener(
  'pageswap',
  (e) => {
    updateViewTransitionTypes(
      e as ViewTransitionEvent,
      ['ai-mode'],
    );
  }
);

这样一来,我们便可防止命名冲突,并避免不必要地对不需要作为进入和退出 AI 模式的一部分进行快照的元素进行快照。

最后,任何堆叠上下文问题都只会出现在视图过渡期间。为了解决这些问题,我们可以定位生成的伪元素的 z-index,而不是随意修改原始元素的 z-index,只为解决使用视图过渡时出现的此问题。

::view-transition-group(the-element) {
  z-index: 100;
}

后续步骤

我们计划将跨文档视图过渡用于 Google 搜索,包括在Navigation API 可跨浏览器使用后将其集成到 Google 搜索中。敬请期待我们接下来会构建什么!