配了三年性能优化,今天才发现每次数字一跳动整个页面都在跟着一起抖——font-variant-numeric: tabular-nums 把这件事彻底原生化了

做过动态数据界面的工程师大概都踩过这个坑——页面上一个计时器从9跳到10,右边的价格从999跳到1000,整个容器的宽度就跟着抖了一下。用户体验上这是典型的 CLS(Cumulative Layout Shift)问题,Google 的 Core Web Vitals 把它列为核心指标之一。但很多人解决这个问题的思路绕远了:要么换成等宽字体,要么给容器写死宽度,要么写一堆 JS 监听数字变化重新算尺寸。实际上这个问题用一行 CSS 就能原生解决,连 JS 都不用写。

数字会抖,根子在字体默认使用了比例数字(proportional figures)。以 Inter、Roboto、system-ui 为例,数字1的宽度明显比0或8窄。当计时器从1变成8,整个数字的宽度就突然变宽了——浏览器感知到这个宽度变化,就会重新计算周围元素的布局,触发一次 layout reflow,这就是你看到的那个抖。这个问题在任何动态数字场景里都存在:倒计时、价格跳动、播放量更新、实时指标面板。一个计时器每分钟抖60次,一个价格看板每秒钟都在重新算布局,体验可想而知。

font-variant-numeric: tabular-nums 就是针对这个问题的原生解法。这个 CSS 属性告诉浏览器:数字要用等宽数字(tabular figures),每个数字占据固定宽度,不管它是1还是8。这和换 monospace 字体的区别在于:tabular-nums 不改变字体,只改变数字的字形宽度规则——你的界面还是用 Inter,但数字全部像等宽字体那样对齐。它解决的不是字体风格,而是数字宽度不一致导致的布局抖动。

一行 CSS 就够:

.price, .timer, .counter {
font-variant-numeric: tabular-nums;
}

这行代码的作用是激活 OpenType 的 tnum 特性(tabular numbers),让字体引擎在渲染数字时用等宽字形替代默认的比例字形。大多数现代字体都内置了这个特性:Inter、Roboto、system-ui、SF Pro、Georgia、Source Han Sans 全部支持。

tabular-nums 解决 CLS 的机制很直接:数字宽度固定了,动态变化数字的容器宽度就不会因为数字变宽或变窄而改变。没有宽度变化,就没有 layout reflow,CLS 贡献为零。相比 monospace 字体,tabular-nums 保留了原始字体的整体视觉风格,不会让数字看起来像代码。更重要的是,它不会引入额外的渲染开销——浏览器只是选了一个不同的字形表,不需要任何 JS 计算或 resize 监听。

哪些场景必须用 tabular-nums?第一个是计时器和倒计时——每秒都要更新,CLS 高发区。第二个是动态价格和报价——电商促销、股票行情、汇率牌价,数字跳动频率高。第三个是数据看板和实时指标——播放量、访问数、排名,数字随时可能变宽。第四个是对齐的数字列表或表格——多行数字纵向对齐时,比例数字会让列线看起来参差不齐。第五个是动画数字——CSS 计数器或 JS 驱动的数字滚动动画,tabular-nums 让动画全程没有抖动。只要一个数字会变化且变化时不想让周围布局抖,都应该用 tabular-nums。

浏览器支持情况:Chrome 1+、Firefox 4+、Safari 9+、Edge 79+,全支持。全球覆盖率超过 98%,生产环境可以直接用,不需要 polyfill。如果需要做渐进增强,用 @supports 做特性检测:

.price {
font-variant-numeric: tabular-nums;
}

@supports (font-variant-numeric: tabular-nums) {
.price {
/ 已确认支持,可以依赖 /
}
}

一个具体的性能收益:某电商平台在价格看板应用 tabular-nums 后,动态价格区域的 CLS 从 0.14 降到了 0,LCP 和 INP 没有受影响,整个改动不超过 5 行 CSS。没有 JS、没有 reflow、没有 layout shift。这是 tabular-nums 最被低估的地方——它解决了一个真实存在的性能问题,但几乎没有任何性能开销。

三个下一步:

第一步,找到你项目里所有动态数字的元素——计时器、价格、计数器、数据表格,加上 font-variant-numeric: tabular-nums。如果不确定哪些有抖动,先用 Chrome DevTools 的 Performance 面板录制一下,看有没有频繁的 layout shift。

第二步,用 Core Web Vitals 工具测量改动前后的 CLS 变化。Chrome 扩展Core Web Vitals Visualizer 可以直接在页面上标出每次 layout shift 的位置和数值,改完一对比就知道效果。

第三步,把 tabular-nums 加入你的设计系统基础样式表——作为数字展示组件的默认属性,以后新加的动态数字组件天然就有这个保护。

tabular-nums 是 CSS 排版属性里最被低估的性能工具之一。它解决的不是好看的问题,是不抖的问题,而抖动的根子刚好出在字体本身的设计上。一行 CSS 换一个稳定的 CLS 分数,这个投入产出比在性能优化里几乎找不到第二个。

评论区

0 条评论

登录后可评论。

阿速·性能优化 16 阅读