配了多年布局稳定性,今天才发现 intrinsic size 一直在靠手算——Safari STP 158 把这件事彻底原生化了

配了多年布局稳定性,今天才发现 intrinsic size 一直在靠手算——Safari STP 158 把这件事彻底原生化了

做过无限滚动、懒加载、动态挂载组件的前端工程师都踩过这个坑:CLS(Cumulative Layout Shift)分数怎么都上不去,根因是浏览器不知道内容区在加载前该占多大空间,骨架屏白块一跳,页面就抖一下。以前解法是手写 min-height、塞占位元素,或者让后端返回明确高度。今天 Safari Technology Preview 158 把 contain-intrinsic-size 的 marked 关键字做进来了——浏览器终于能自己从 DOM 里读出正确的高度了。

以前怎么算 intrinsic size

contain-intrinsic-size 用来告诉浏览器「内容加载完成后,这个元素大概有多大」,让浏览器在内容还没进来的时候提前留出空间,减少跳移。这个属性其实 2022 年就有了,但有两个问题:

问题一:你要自己量高度

.card {
  /* 手动量出设计稿里卡片的典型高度 200px,写死 */
  contain-intrinsic-size: 100px 200px;
}

换一个卡片尺寸就要改 CSS,不同卡片高度不一样还要写一堆 class。线上 A/B 测试一变,布局就抖。

问题二:旧关键字不够用

contain-intrinsic-size: auto 400px —— 前者是「浏览器记住上次渲染的实际高度」,后者是「永远用 400px」。前者不适合首屏(没历史记录),后者不够精确。

今天 marked 把计算自动化了

Safari Technology Preview 158(WebKit r256138)引入 contain-intrinsic-size: marked 关键字,配合 HTML 属性 intrinsicsize(对应 JS 的 elementIntrinsicSize),让开发者声明「哪个子元素代表内容高度」,浏览器自动从中提取 intrinsic size。

<!-- 骨架屏阶段,浏览器知道内容主体是 .card-body -->
<div class="card" intrinsicsize="240px">
  <div class="card-body">动态加载的内容</div>
</div>
/* CSS 端声明引用关系 */
.card {
  contain-intrinsic-size: marked; /* 浏览器从 intrinsicsize 属性读高度 */
}

浏览器工作流程:

  1. 解析 HTML,遇到 intrinsicsize="240px"
  2. 在 contain-intrinsic-size: marked 的元素上,用 240px 作为 intrinsic size
  3. 渲染树在骨架屏阶段就留出 240px 高度,不依赖 CSS 里的硬编码数值
  4. 内容加载渲染后,实际内容替换骨架高度,零跳移

完整实战例子

<!-- 动态加载的文章卡片 -->
<div class="article-card" intrinsicsize="300px">
  <!-- 骨架屏(visibility:hidden内容撑开高度) -->
  <div class="skeleton">
    <div class="skeleton-title"></div>
    <div class="skeleton-meta"></div>
  </div>
</div>

<style>
.article-card {
  contain-intrinsic-size: marked; /* 引用 HTML 属性里的高度 */
  /* 配合旧的 auto fallback,没 intrinsicsize 时用历史记录 */
  contain-intrinsic-size: auto 300px;
}

.skeleton {
  visibility: hidden; /* 占据渲染空间但不可见 */
}
.skeleton-title { height: 24px; }
.skeleton-meta  { height: 16px; margin-top: 8px; }
</style>

<script>
// 懒加载内容
async function loadCard(card) {
  const content = await fetch('/api/card-content').then(r => r.text());
  card.querySelector('.skeleton').outerHTML = content;
  // intrinsicsize 属性配合 marked,骨架→内容零跳移
}
</script>

浏览器支持

浏览器 支持情况
Safari Safari STP 158+(本功能)
Safari 稳定版 ❌ 尚未支持
Chrome/Edge ❌ 暂无信号
Firefox ❌ 暂无信号

WebKit 的实现走在其他引擎前面,稳定版支持时间待观察。渐进增强方案:在不支持的浏览器里,contain-intrinsic-size: auto 300px 自动降级为历史记录模式。

三步落地

  1. HTML 属性标注:给骨架屏容器加 intrinsicsize="<预期高度>"
  2. CSS 声明引用:写 contain-intrinsic-size: marked,浏览器自动从 HTML 属性读值
  3. 降级兜底:加一条 contain-intrinsic-size: auto <fallback>,未支持浏览器走历史高度
.dynamic-component {
  contain-intrinsic-size: marked;       /* 新语法,自动化 */
  contain-intrinsic-size: auto 200px;  /* 降级:历史高度或默认值 */
}

marked 这个关键字的本质是把「高度信息」从 CSS 的硬编码里解放出来,回归到声明内容本身上——骨架屏知道自己的高度,浏览器根据声明自己算 CLS。Safari STP 158 先走一步,稳定版出来后就可以作为骨架屏场景的标准方案上了。

评论区

0 条评论

登录后可评论。

阿柯·前端架构 153 阅读