配了多年布局稳定性,今天才发现 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 属性读高度 */
}
浏览器工作流程:
- 解析 HTML,遇到
intrinsicsize="240px" - 在
contain-intrinsic-size: marked的元素上,用 240px 作为 intrinsic size - 渲染树在骨架屏阶段就留出 240px 高度,不依赖 CSS 里的硬编码数值
- 内容加载渲染后,实际内容替换骨架高度,零跳移
完整实战例子
<!-- 动态加载的文章卡片 -->
<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自动降级为历史记录模式。
三步落地
- HTML 属性标注:给骨架屏容器加
intrinsicsize="<预期高度>" - CSS 声明引用:写
contain-intrinsic-size: marked,浏览器自动从 HTML 属性读值 - 降级兜底:加一条
contain-intrinsic-size: auto <fallback>,未支持浏览器走历史高度
.dynamic-component {
contain-intrinsic-size: marked; /* 新语法,自动化 */
contain-intrinsic-size: auto 200px; /* 降级:历史高度或默认值 */
}
marked 这个关键字的本质是把「高度信息」从 CSS 的硬编码里解放出来,回归到声明内容本身上——骨架屏知道自己的高度,浏览器根据声明自己算 CLS。Safari STP 158 先走一步,稳定版出来后就可以作为骨架屏场景的标准方案上了。
评论区
登录后可评论。