最近在重构一个移动端页面时,我偶然发现 CSS 的世界里又多了一批以前没怎么见过的新单位。翻开规范文档一查,除了熟悉的vw、vh、rem,还冒出svh、lvh、dvh、cqi、cqw这些看名字眼熟、用起来却拿不准的单位。再往下看,居然连cap、ic、lh、rlh、q都排着队出现了。说实话,一不留神,CSS 新单位的名单已经长到能让一页笔记写满。
不过话说回来,单位多不代表每个都值得用。这篇文章我不打算把每一个 CSS 新单位都拎出来背一遍属性表,而是先把目前能接触到的单位按类别梳理清楚,再重点聊聊哪些单位能真正解决日常开发里的痛点。尤其是移动端高度抖动、组件级响应式、文字排版对齐这些高频场景,我会配可运行的代码示例一步步拆开讲。
如果你是刚接触 CSS 新单位的新手,可以通过这篇文章建立一个整体认知;如果你已经写过一些vh、rem,那可以重点看第 4 节的实用示例和第 5 节的排查思路,快速筛选出适合进入项目的单位。
1. 背景与核心概念
1.1 为什么 CSS 单位突然“变多”了
CSS 的单位并没有一下子在某个版本里集体爆发,而是随着 Web 场景越来越复杂,被规范一点点补全的。早期的 CSS 只需要应对 PC 浏览器里相对固定的页面布局,px、%、em就已经够用。后来移动端兴起,需要适配不同屏幕宽度,于是出现了vw、vh、vmin、vmax这类视口单位。
再往后,问题变得更细:
- 移动端浏览器的地址栏会收起和弹出,导致
100vh在不同滚动状态下高度不一致; - 组件化开发越来越流行,很多组件希望根据“父容器”的宽度而不是“视口”的宽度来变化;
- 排版要求越来越精细,需要按行高、字符宽度甚至大写字母高度来对齐;
- 国际化和中文排版场景增多,需要更适合 CJK 字符的度量单位。
这些需求催生了一大批新单位。它们并不是凭空冒出来的“炫技功能”,而是在补齐 CSS 在视口、容器、字体排版几个维度上的能力缺口。
1.2 新旧单位的关系
理解 CSS 新单位时,可以先把它分成几个家族:
| 家族 | 旧单位 | 新单位 | 核心变化 |
|---|---|---|---|
| 视口单位 | vw、vh、vmin、vmax | svh、lvh、dvh、svi、lvi、dvi 等 | 区分小视口、大视口、动态视口 |
| 容器查询单位 | 无 | cqw、cqh、cqi、cqb、cqmin、cqmax | 相对最近的容器尺寸计算 |
| 字体相对单位 | em、rem、ex | ch、ic、cap、lh、rlh | 更细粒度地描述字符和行盒子 |
| 绝对单位 | px、mm、cm | q | 四分之一毫米,印刷场景用 |
可以看到,新单位不是把旧单位推翻,而是在旧单位的基础上增加“更精确的参考系”。比如vh只有一个固定参考系,而svh、lvh、dvh分别对应小视口、大视口和动态视口,提供了三种不同视角的高度参考。
2. 环境准备与兼容性说明
2.1 本文示例的运行环境
本文的代码示例以“现代浏览器”为前提,建议在下面环境中运行:
- 操作系统:Windows / macOS / Linux 均可
- 浏览器:Chrome、Edge、Firefox、Safari 的较新版本
- 本地开发:VS Code + Live Server,或者任意静态服务器
- 构建工具:本文示例是纯 HTML/CSS,不需要额外构建,复制到
.html文件即可预览
需要特别说明的是,CSS 新单位的浏览器兼容性差别很大。像dvh、svh这类视口单位已经在多数现代浏览器中得到支持,但cqi、cqw这类容器查询单位虽然逐步铺开,仍有版本差异。更冷门的cap、ic、rlh则需要仔细确认目标用户使用的浏览器版本。
2.2 兼容性查询方法
在项目里使用一个 CSS 新单位之前,我最推荐的做法是打开 Can I Use 网站查一下。以dvh为例:
https://caniuse.com/viewport-unit-variants如果某个单位在你的目标浏览器中支持率不理想,就按下面两个方向处理:
- 先写一个常用的兜底值,再写新单位,让浏览器优先使用它认识的新单位;
- 如果新单位只是增强效果,而不是关键功能,那就把它当作渐进增强,不要影响核心布局。
版本需要根据项目实际情况调整,本文重点演示的是配置思路和用法,不是某个固定版本下的绑定语法。
3. 新单位全景:先看看都有谁
这一节先把目前能见到的 CSS 新单位按类别过一遍。不需要立刻记住每一个的换算细节,先建立一个“有哪些、大致在什么场景用”的框架。
3.1 视口相关新单位
传统视口单位vh有一个常年被吐槽的问题:在移动端浏览器里,100vh不一定等于用户当前看到的视口高度,因为浏览器地址栏会占据一部分空间,而且会在滚动时收起。不同的浏览器对“视口高度”的定义也不同,这就导致100vh布局在部分场景下底部被遮挡,或者出现滚动跳动。
CSS Values and Units Module Level 4 引入了一组新的视口单位:
svh:小视口高度,表示浏览器工具栏全部展开时的高度;lvh:大视口高度,表示浏览器工具栏全部收起时的高度;dvh:动态视口高度,会随着工具栏状态实时变化;svw、lvw、dvw:对应宽度方向的小视口、大视口、动态视口;svi、lvi、dvi和svb、lvb、dvb:分别是内联方向和块方向的视口单位。
在这些单位中,dvh和svh是目前实用价值最高的。dvh适合绝大多数“撑满整个屏幕”的场景,因为它能跟随浏览器工具栏的变化自动调整;svh适合一些不希望因为工具栏状态变化而跳动的布局,比如底部的固定操作栏。
3.2 容器查询单位
容器查询是近几年 CSS 的重要能力之一。传统媒体查询@media只能根据视口宽度来改变样式,但实际开发中,一个组件放在侧边栏是 300px,放在主区域是 800px,我们希望组件能根据“父容器宽度”自适应,而不是被视口宽度绑架。
容器查询单位就是配合container-type属性使用的:
cqw:最近容器宽度的 1%;cqh:最近容器高度的 1%;cqi:最近容器内联方向尺寸的 1%;cqb:最近容器块方向尺寸的 1%;cqmin:cqi和cqb中较小的一个;cqmax:cqi和cqb中较大的一个。
要启用容器查询,父元素需要设置container-type,常用值是inline-size,表示只让容器在水平方向参与查询。如果不设置这一条,子元素里的cqi、cqw单位是不会生效的。
3.3 字体排印类单位
字体相对单位原本是em和rem的天下,但这两个单位在精细排版中并不够用。后来 CSS 补全了下面几个单位:
ch:字符 “0” 的宽度。这个单位适合控制等宽字段的宽度,例如验证码输入框、序列号展示框;ic:CJK 表意字符的宽度,常用于中文排版场景;cap:当前字体大写字母的高度,适合做标题或首字母相关的排版;lh:当前元素line-height的计算值,适合做与文字行高对齐的样式;rlh:根元素line-height的计算值,适合全站统一的行高基准。
其中lh和rlh在最近的开发中比较常见。比如想要绘制一条与某行文字高度完全一致的背景条,直接使用height: 1lh会比拍脑袋写死px值更可靠。
3.4 其他容易被忽略的单位
除了上面几类,CSS 还定义了一些相对冷门的单位。比如q,全称是 quarter-millimeter,表示四分之一毫米,即1q = 0.25mm。它的主要应用场景是印刷样式,在屏幕端几乎用不到。
另外还有一些开发者容易混淆的“新能力”,比如min()、max()、clamp()、round()、mod()、rem()这些数学函数。它们不是单位,但经常和新单位一起出现在讨论中。clamp()可以配合vw、rem做自适应字号,例如:
font-size: clamp(1rem, 2vw + 1rem, 2rem);这类函数能让新单位在真实布局中发挥更大的作用,建议一起学习,但不要把它们归类成“CSS 新单位”。
4. 实用单位实战:从“会读”到“会用”
名字大概认识了,接下来进入重点。这一节挑出几个真正能改善日常开发的单位,配合完整示例讲用法。
4.1 dvh / svh:解决移动端高度抖动
先看一个最常见的移动端布局:底部操作栏 + 容器占满剩余空间。
传统写法通常是这样:
/* 文件路径:demo/dvh-demo.html */ html, body { margin: 0; height: 100%; } .page { height: 100vh; display: flex; flex-direction: column; } .content { flex: 1; overflow-y: auto; } .footer { height: 60px; background: #4f6ef7; color: #fff; display: flex; align-items: center; justify-content: center; }在移动端浏览器中,地址栏展开时100vh会比可视区域高出一截,导致底部操作栏被顶到屏幕外;地址栏收起时又会出现跳动。改用动态视口单位后,页面高度会跟随浏览器工具栏状态自动调整:
.page { height: 100vh; height: 100dvh; display: flex; flex-direction: column; }先写100vh作为兜底,再写100dvh覆盖。支持dvh的浏览器会使用动态高度,不支持的老浏览器会忽略第二条声明,继续使用100vh。
如果希望页面保持稳定,不跟随工具栏变化,可以使用svh:
.fixed-page { height: 100svh; overflow: hidden; }这个写法适合“固定一屏,不滚动”的页面,例如一些活动 H5 的首页。
4.2 cqi / cqw:真正让组件自适应
容器查询单位最大的价值,是让组件在它被放置的容器里按比例缩放。下面的例子模拟一个商品卡片,它可能在左侧边栏(窄容器)里显示,也可能在右侧主内容区(宽容器)里显示。
先准备一个带容器的页面:
<!-- 文件路径:demo/container-demo.html --> <!DOCTYPE html> <html lang="zh-CN"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>容器查询单位示例</title> <link rel="stylesheet" href="container-demo.css"> </head> <body> <div class="wrapper"> <div class="card"> <h2 class="card-title">威士忌品鉴套装</h2> <p class="card-desc">精选单一麦芽,适合入门爱好者</p> <span class="card-price">¥ 368</span> </div> </div> </body> </html>核心 CSS:
/* 文件路径:demo/container-demo.css */ .wrapper { container-type: inline-size; width: 100%; max-width: 900px; margin: 0 auto; } .card { padding: 16px; border: 1px solid #e5e7eb; border-radius: 12px; } .card-title { font-size: clamp(1rem, 4cqi, 2rem); } .card-desc { font-size: clamp(0.875rem, 2.5cqi, 1rem); } .card-price { font-size: clamp(1rem, 3cqi, 1.75rem); color: #e11d48; }这里的4cqi表示容器内联方向尺寸的 4%。容器宽度越大,字号就越大;容器宽度变小,字号也成比例缩小。配合clamp()设置最小值和中值,可以防止字号在极端情况下变得过大或过小。
值得注意的坑是:container-type: inline-size放在父级.wrapper上,才能让.card内部的cqi生效。如果容器没有这一行配置,cqi会退化为无效值,浏览器会忽略这条样式。
4.3 lh / rlh:用行高做排版对齐
在做图文排版时,经常想画一个和文字行高完全吻合的图标,或者做一个和文字同高的背景条。用px需要反复量字体大小和行高,而且换字体就失效。这时候lh单位就非常合适。
下面示例演示一个消息列表,每条消息左侧有一个圆形状态点,希望圆点高度和第一行文字高度保持一致:
<!-- 文件路径:demo/lh-demo.html --> <ul class="msg-list"> <li class="msg-item"> <span class="dot"></span> <span class="msg-text">订单已发货,预计明天送达,请保持手机畅通。</span> </li> </ul>/* 文件路径:demo/lh-demo.css */ .msg-list { list-style: none; padding: 0; margin: 0; } .msg-item { display: flex; align-items: center; gap: 8px; padding: 12px 16px; font-size: 16px; line-height: 1.6; } .dot { width: 1lh; height: 1lh; border-radius: 50%; background: #22c55e; flex-shrink: 0; }1lh等于当前元素line-height计算值,因此圆点会像“贴着文字行高”一样与文字高度保持一致。如果文字字体、行高在断点处变化,圆点尺寸也会自动跟随。
rlh则与根元素行高绑定,适合全局统一的间距体系:
.card { margin-bottom: 1rlh; }这表示卡片底部间距等于根元素的一倍行高。只要根字号和全局行高不变,所有使用rlh的间距就天然保持一致。
不过要注意,lh和rlh在部分浏览器中的支持情况仍有差异,涉及关键布局时建议提供px或者em兜底。
4.4 ch:精确控制等宽字段宽度
ch并不是最近才出现的单位,但很多开发者并没有真正理解它的用途。它的参考基准是当前字体中字符 “0” 的宽度,因此在等宽字体下,10ch基本等于 10 个字符的宽度。
实战场景:设计一个邀请码输入框,约定最多 6 位字符。传统做法会用width: 240px估算,但换字体、换字号就会失真。用ch则可以直接贴近字符数:
<!-- 文件路径:demo/ch-demo.html --> <label class="code-field"> <span>邀请码</span> <input class="code-input" type="text" maxlength="6" placeholder="ABCDEF"> </label>/* 文件路径:demo/ch-demo.css */ .code-field { display: flex; flex-direction: column; gap: 8px; font-family: 'Courier New', Courier, monospace; } .code-input { width: 8ch; padding: 8px 12px; font-size: 20px; font-family: inherit; letter-spacing: 2px; border: 1px solid #d1d5db; border-radius: 8px; }width: 8ch加上 padding,可以让输入框宽度正好容纳 6 个字符左右,视觉上很均衡。但要注意,中文和全角标点的宽度并不等于 “0” 的宽度,所以ch不适合用来约束中文输入框。如果需要处理中文场景,可以考虑ic单位,它表示 CJK 表意字符的宽度。
5. 常见问题与排查思路
新单位看着美好,真正落到项目里时,经常遇到各种“不生效”或“表现奇怪”的情况。下面把常见问题列出来,方便日后排查。
5.1 新单位不生效
| 问题现象 | 常见原因 | 解决思路 |
|---|---|---|
dvh不生效 | 浏览器版本过旧,不支持动态视口单位 | 先写height: 100vh兜底,再写height: 100dvh |
cqi不生效 | 父级没有设置container-type | 检查最近的容器是否写了container-type: inline-size |
lh不生效 | 浏览器不支持相对行高单位 | 用em或px兜底,并将新单位作为增强 |
| 写了新单位但整条样式被忽略 | 拼写错误或浏览器不认识该单位 | 打开浏览器控制台,查看样式是否被划掉 |
排查这类问题时,建议直接打开开发者工具,选中目标元素,看 Computed 面板中该样式是否被解析。如果被解析但效果不明显,再看是不是被其他规则覆盖了。
5.2 容器查询单位无效
容器查询单位无效是出现频率最高的问题。先检查一下最近的祖先元素有没有设置container-type。只设置container-type: inline-size才能让子元素使用宽度方向的容器查询单位,如果设置的是container-type: size,容器的高度也需要明确,否则可能表现异常。
另一个容易被忽略的问题是:容器查询单位只能在“容器子孙元素”里使用,不能应用到容器自身。也就是说,下面的写法是无效的:
/* 错误写法:容器查询单位不能用在容器自身 */ .card-container { container-type: inline-size; width: 50cqi; }正确做法是把50cqi写到.card-container里面的子元素上。
5.3 移动端高度仍然抖动
用了dvh之后,部分安卓 WebView 或旧版浏览器仍然会出现高度抖动。原因是不同浏览器对“动态视口”的更新时机和规则定义不完全一致。
解决办法是采用“三明治兜底法”:
.page { height: 100vh; /* 兜底 */ height: 100svh; /* 稳定的小视口高度 */ height: 100dvh; /* 支持动态视口时使用 */ }如果你的布局需要“尽量撑满屏幕且希望稳定”,用svh比dvh更稳。如果希望跟随工具栏状态变化获得更多空间,就用dvh。
5.4 单位混用导致计算异常
CSS 新单位之间并不总是可以直接混入calc()进行运算,这取决于浏览器对单位解析的支持程度。例如:
width: calc(100cqi - 32px);这类写法在现代浏览器中已经能够工作,但在老旧浏览器中会被整体忽略。对于关键布局,建议先用百分比或px把主结构定好,再考虑用cqi做细节增强。
5.5 排查清单
遇到 CSS 新单位相关的疑难杂症时,可以按照下面的顺序排查:
- 检查单位拼写是否准确;
- 检查目标元素是否处于正确的“参考系”内(例如容器查询需要容器祖先);
- 打开开发者工具,确认样式是否被浏览器解析并生效;
- 查看兼容性列表,确认目标浏览器是否支持该单位;
- 先加一层传统的
px、%、vh兜底; - 用最小化复现法,把样式缩小到只有新单位的那一条,从复杂页面中抽离出来单独验证。
6. 最佳实践与工程建议
6.1 把“兼容性检查”放在第一位
新单位进入项目前,不要只看它能不能在本地 Chrome 里跑。最好看看产品的用户分布,尤其是移动端用户,是否集中在旧版本浏览器或老旧 WebView。如果核心布局严重依赖不兼容的新单位,宁可不用,也不要为了新技术冒险。
6.2 兜底写法是基本素养
任何新单位都应该有一条老单位作为兜底。通用的写法是“老值在前,新值在后”:
height: 100vh; height: 100dvh;浏览器会优先选择它认识的后一条声明,不认识的则跳过。这样既不影响老浏览器,也能让新浏览器获得更好的表现。
6.3 用 CSS 变量封装新单位
团队协作时,直接把cqi、svh散落在各处,很容易让其他同事看不懂。建议把关键的新单位封装成有语义的 CSS 变量:
:root { --app-height: 100vh; --card-title-size: clamp(1.25rem, 4cqi, 2.5rem); } @supports (height: 100dvh) { :root { --app-height: 100dvh; } } .page { min-height: var(--app-height); }这样使用侧只需要关心变量名,不需要理解底层用的是哪类单位。后续如果规范变化,只需要在变量定义处统一调整。
6.4 只在渐进增强场景中使用
新单位更适合作为“渐进增强”的手段,而不是唯一的布局依赖。比如:
- 核心布局先用
flex、grid、%和vh搭好; - 需要用新单位微调的细节,例如底部高度、字体大小、间距,再基于新单位覆盖。
这样即使浏览器不支持新单位,页面也不会出现结构性坍塌。
6.5 团队协作时留注释
新单位终究不像px、%那样人人熟悉。在代码里写清楚为什么用这个单位,能减少后续维护成本:
/* * 使用 svh 作为兜底: * 当浏览器支持 dvh 时,动态高度体验更佳; * 不支持时,svh 可以稳定地占满小视口。 */ .page { height: 100svh; height: 100dvh; }一段清晰的注释,往往比文档中心还能更快地帮助同事理解你的设计意图。
6.6 关注规范更新,但别追新内卷
CSS 相关规范仍在持续演进,未来可能还会有新的单位出现。建议保持关注,但不要为了“用新单位”而用。一个单位是否进入项目,应该取决于它能否解决实际问题,而不是因为它符合“新技术潮流”。
7. 总结与下一步
CSS 新单位并不是一个孤立的知识点,它背后是视口适配、容器查询、字体排版规范这几个方向的同步演化。从dvh解决移动端高度抖动,到cqi、cqw让组件级响应式成为可能,再到lh、ch带来的排印控制力,真正成熟的单位进入项目后,能明显减少硬编码和魔法数值。
下一步建议你动手试试这几件事:
- 打开一个移动端项目,把某处的
100vh改写成100svh+100dvh的兜底组合,观察地址栏收起和展开时的表现; - 把一个卡片组件的父容器加上
container-type: inline-size,用cqi替代部分媒体查询; - 用
lh写一次消息列表的状态点,感受行高对齐的便利。
如果你也想在评论区分享自己的使用经验,或者遇到了其他奇怪的新单位问题,欢迎留言讨论。这篇文章里的示例代码我放在本地一个demo文件下,后边有需要可以直接复制改造成自己的场景。如果觉得这篇梳理有用,可以收藏备用,等真正遇到对应需求时再拿出来对照。