通过状态缓存实现更快的 SPA 视图过渡

Web 功能: content-visibility

通过缓存非活动视图的渲染状态(而不是销毁它们),在单页应用 (SPA) 中的视图之间实现即时导航。

概览

过去,当用户在 SPA 中的标签页或视图之间切换时,开发者要么销毁旧视图,要么使用 display: none 将其隐藏。这两种方法都需要浏览器在用户返回到该视图时重新创建或重新计算完整布局并重新绘制。

通过在非活动视图上使用 content-visibility: hidden,浏览器会从布局流中移除元素的内容并停止绘制,但会保留其在内存中的缓存渲染状态。当用户切换回来时,视图几乎会立即恢复。

CPU 与 RAM 的权衡

虽然这种方法可以带来巨大的性能优势,但也会带来一个特定的权衡,您必须谨慎管理:

  • CPU 节省:大幅节省。浏览器会完全跳过隐藏视图的布局和绘制过程。
  • RAM 费用:高。浏览器会将隐藏视图的所有 DOM 节点、事件监听器和状态保留在内存中。

这种权衡何时会变得危险

  • 可以将此策略用于视图数量较少且可预测的简单应用(例如,具有 3 到 5 个标签页的界面)。
  • 请勿在高度动态的应用中无条件缓存每个视图,因为这些应用会生成数十个唯一报告或使用无限动态路由。这样做最终会导致严重的内存膨胀,并可能导致低端设备上的浏览器崩溃。
  • 强制性要求:如果您的应用高度动态,您必须实现逐出策略(例如“最近最少使用”缓存),以便在达到内存阈值时销毁旧视图。

实现

1. 配置视图容器

在当前未处于活动状态的视图上设置 content-visibility: hidden。

.spa-view.inactive {
  /* MANDATORY: Use hidden to cache the rendering state of inactive views */
  content-visibility: hidden;
  
  /* Optional: Prevent hidden views from taking up physical space in layout flow */
  position: absolute;
}

2. 管理专注模式

切换视图时,请确保正确管理键盘焦点,以保持无障碍性。

function switchToView(viewId) {
  // Hide all views
  document.querySelectorAll('.spa-view').forEach(view => {
    view.classList.add('inactive');
    view.setAttribute('aria-hidden', 'true');
  });
  
  // Show the target view
  const activeView = document.getElementById(viewId);
  activeView.classList.remove('inactive');
  activeView.setAttribute('aria-hidden', 'false');
  
  // MANDATORY: Move focus to the new view to ensure a logical tab-order
  activeView.focus();
}

后备策略

content-visibility 属性会正常降级。在不支持该功能的浏览器中:

  • 该属性会被忽略。
  • 为防止后备浏览器同时呈现所有视图,您应在 CSS 中提供 display: none 的后备:
@supports not (content-visibility: hidden) {
  .spa-view.inactive {
    display: none;
  }
}