发布清单
整理发布负责人、交付内容与恢复条件。
CSS content-visibility: auto 可以让浏览器跳过当前与用户无关的屏外内容子树的部分渲染工作,包括布局和绘制,从而减少长列表首屏需要处理的内容。实际使用时,把它加到独立卡片或列表分组,再配合合理的占位尺寸;不要只加在始终横跨视口的整个列表容器上。
它保留 DOM,也不负责取消数据请求或组件初始化。如果首屏慢的主要原因是大量 JavaScript、组件挂载或数据处理,这个 CSS 属性不能代替分页、虚拟列表或按需初始化。本文用原创资料列表场景说明设计取舍,代码未在本文环境运行,不给出虚构的优化比例。
CSS Containment 官方规范地址:https://drafts.csswg.org/css-contain-2/
假设资料页已经加载许多卡片,每张卡片包含标题、摘要、标签和操作链接。用户打开时只看到开头几张,但普通布局仍可能涉及大量屏外子树。列表越长,卡片内部越复杂,首屏之外的渲染工作就越值得关注。
我会先把成本分成三组:数据与 JavaScript、DOM 数量、布局与绘制。content-visibility: auto 主要帮助第三组,DOM 仍然存在,应用代码也不会自动停止。这样才能解释为什么有的页面加上属性后变顺畅,有的页面长任务却几乎没变化。
浏览器决定哪些内容与用户相关,不只是判断元素是否严格进入视口。接近视口的内容可以提前准备,焦点、选择等状态也会影响处理。因此,不应把它当作精确到某一像素距离的业务触发器。CSS Containment 对用户相关内容的定义
对于卡片高度差异较大的资料页,可以先把列表按相近内容分组,例如每组数张条目。分组太大,首屏一旦与该组相交,组内大批内容就需要处理;粒度过细则增加大量管理边界。不存在适用于所有页面的固定条数,应按内容复杂度和布局关系选择。
下面保留外层页面标题与导航,把每个资料分组作为独立子树。代码用少量条目展示结构,真实数据由原有服务端模板或组件生成,不需要为了 CSS 优化改变内容顺序。
项目资料目录
交付资料
发布清单
整理发布负责人、交付内容与恢复条件。
归档约定
说明资料命名与归档位置,便于后续查找。
维护资料
维护窗口约定
记录维护影响范围与联系渠道。
示例链接是站内路由占位,应替换为项目真实地址。这里采用完整、可阅读的普通文档结构;支持优化的浏览器可以跳过屏外分组,不支持时仍正常展示。

独立内容块的做法也与规范的粒度建议一致。关键不在给每个元素批量加属性,而在找到内部渲染工作较多、对外部布局依赖清楚的边界。
浏览器跳过子树时,仍需知道外部布局应给这个分组留多少空间。若没有合适的占位,滚动条长度和附近位置可能在内容恢复渲染时明显变化。这里使用 contain-intrinsic-size 提供替代内在尺寸。
/* 基础布局始终可用,不支持优化属性也能读完列表。 */
.resource-chunk {
margin-block: 0 1.5rem;
padding: 1rem;
border: 1px solid #d6dce5;
border-radius: 0.75rem;
}
.resource-card + .resource-card {
margin-block-start: 1rem;
}
/* 两个属性共同可用时才开启;数值是本示例的起始估计。 */
@supports (content-visibility: auto) and
(contain-intrinsic-size: auto 900px) {
.resource-chunk {
content-visibility: auto;
contain-intrinsic-size: auto 900px;
}
}
/* 打印时采用普通内容布局,便于按实际文档检查分页。 */
@media print {
.resource-chunk {
content-visibility: visible;
contain-intrinsic-size: none;
}
}
auto 900px 不是强制把真实分组高度锁死在 900px。通常渲染过的分组可以记住自身尺寸,之后再次被跳过时使用记忆值;尚无记忆值时使用给定长度。该简写中的一组值作用于宽高两个维度,本例普通块的宽度由容器确定,重点关注纵向占位;若改用网格、横向列表或多栏布局,就要重新考虑两个维度的影响。占位尺寸与记忆值说明
900px 只是代码演示值,不是推荐所有列表照搬的常量。估计过小,进入该分组后需要扩展较多空间;估计过大,滚动条可能先给出不真实的长度。可先按分组条目数、常见摘要长度和视口宽度估算,再观察不同内容长度的表现。图片若会参与高度变化,还应独立提供可靠尺寸,不能指望此属性处理所有资源引起的位置变动。
一个常见反例是:CSS 刚把屏外分组设为可跳过,初始化代码又遍历每张卡片,读取几何尺寸来计算动画或排版。某些布局读取会迫使浏览器完成原本希望延后的工作,收益因此被抵消。web.dev 特别提醒,不要随意对被跳过子树调用会强制渲染的 DOM API。屏外渲染与脚本读取注意事项
下面是需要重新审视的模式,不是推荐用法。若业务只需要条目数量,就读取数据模型;若真的需要尺寸,应将测量局限于当前必要的区域,并统一安排读写。
// 反例:首屏启动时测量全部卡片,可能迫使屏外子树参与布局。
const cards = document.querySelectorAll(".resource-card");
const heights = Array.from(cards, (card) => {
return card.getBoundingClientRect().height;
});
// 数量来自集合即可;不需要为计数测量每张卡片。
const cardCount = cards.length;
不要把“使用了某个 API”直接等同于“一定每次都重新布局”;是否触发工作取决于调用对象与渲染状态。真正应避免的是在每次滚动、每次列表刷新时都无条件测量全部内容,并交替修改样式。
如果分组还包含应用自行维护的绘制任务,可以考虑 contentvisibilityautostatechange 事件,用 skipped 状态控制该分组可暂停的附属工作。这个事件不会自动暂停定时器或业务请求,也不应被当作内容可以丢弃的信号。需单独处理事件支持与恢复时的数据状态。自动跳过状态事件
对于“DOM 已经存在、卡片内容较复杂、屏外渲染明显”的页面,按分组添加这个属性是一种改动较小的尝试。但数据持续累积、DOM 数量本身已经成为问题时,继续保留全部条目并不会因为屏外跳过就不占内存。
| 主要问题 | 可考虑的手段 | 需要接受的代价 |
|---|---|---|
| 屏外子树布局与绘制较重 | content-visibility: auto | 管理占位与包含边界 |
| DOM 条目数量持续增长 | 虚拟列表 | 维护滚动位置、条目复用与查找交互 |
| 初次传输和数据解析过多 | 分页或按需加载 | 设计加载、错误与返回位置 |
| 组件初始化占用大量主线程 | 减少初始化或按需挂载 | 管理初始化时机和状态恢复 |
这些手段可以组合,但需要知道每一层改变了什么。例如分页控制数据总量,已加载页面内部再按分组减少屏外渲染;若已经使用成熟虚拟列表,就先理解它的测量和缓存机制,不应机械叠加后期待收益相乘。

集成前后应使用相同数据、相近缓存条件与同一类设备比较。重点观察首屏主线程中的布局与绘制时间、滚动接近新分组时的停顿、滚动条变化、DOM 数量以及初始化长任务。如果布局减少但 JavaScript 长任务没有减少,这与方案边界一致,后续优化应回到脚本与数据层。
以上是读者在自身项目中的观察方法,不是本文已经跑出的测量结果。没有目标设备的数据,就不能把第三方演示的倍数写成本项目效果。
auto 会把屏外内容从无障碍树里移除吗? auto 保留 DOM 与无障碍可访问性,并允许浏览器查找等工具与内容交互;它不是隐藏敏感信息的方式。不要为了优化给整个分组统一添加 aria-hidden。原本应隐藏的内容则应独立核对语义与隐藏策略。content-visibility 语义与可访问性
可以用 hidden 代替 auto 吗? 不可以按同一个用途处理。hidden 是主动跳过内容的另一种状态,auto 才是让浏览器根据相关性决定是否渲染。本例保留浏览和查找能力,不做 hidden 切换。
为什么下拉菜单或悬浮层出现变化? 包含边界可能影响溢出绘制、定位和层叠关系。跨卡片的浮层应检查自身挂载位置,必要时放到合适的上层容器;不要直接把全站包含浮层的祖先设成优化块。
旧浏览器不支持怎么办? 示例通过 @supports 渐进增强,基础文档照常可读,差别是没有这项渲染优化。交付目标中的旧 WebView、打印和键盘导航仍应单独关注,不应只根据桌面浏览器结果做保证。
CSS anchor-size() 怎样让浮层尺寸跟随锚点元素