CSS新单位实战:从dvh到cqi,解锁现代响应式布局
2026/9/3 17:30:24 网站建设 项目流程

最近在重构一个移动端页面时,我偶然发现 CSS 的世界里又多了一批以前没怎么见过的新单位。翻开规范文档一查,除了熟悉的vwvhrem,还冒出svhlvhdvhcqicqw这些看名字眼熟、用起来却拿不准的单位。再往下看,居然连capiclhrlhq都排着队出现了。说实话,一不留神,CSS 新单位的名单已经长到能让一页笔记写满。

不过话说回来,单位多不代表每个都值得用。这篇文章我不打算把每一个 CSS 新单位都拎出来背一遍属性表,而是先把目前能接触到的单位按类别梳理清楚,再重点聊聊哪些单位能真正解决日常开发里的痛点。尤其是移动端高度抖动、组件级响应式、文字排版对齐这些高频场景,我会配可运行的代码示例一步步拆开讲。

如果你是刚接触 CSS 新单位的新手,可以通过这篇文章建立一个整体认知;如果你已经写过一些vhrem,那可以重点看第 4 节的实用示例和第 5 节的排查思路,快速筛选出适合进入项目的单位。

1. 背景与核心概念

1.1 为什么 CSS 单位突然“变多”了

CSS 的单位并没有一下子在某个版本里集体爆发,而是随着 Web 场景越来越复杂,被规范一点点补全的。早期的 CSS 只需要应对 PC 浏览器里相对固定的页面布局,px%em就已经够用。后来移动端兴起,需要适配不同屏幕宽度,于是出现了vwvhvminvmax这类视口单位。

再往后,问题变得更细:

  • 移动端浏览器的地址栏会收起和弹出,导致100vh在不同滚动状态下高度不一致;
  • 组件化开发越来越流行,很多组件希望根据“父容器”的宽度而不是“视口”的宽度来变化;
  • 排版要求越来越精细,需要按行高、字符宽度甚至大写字母高度来对齐;
  • 国际化和中文排版场景增多,需要更适合 CJK 字符的度量单位。

这些需求催生了一大批新单位。它们并不是凭空冒出来的“炫技功能”,而是在补齐 CSS 在视口、容器、字体排版几个维度上的能力缺口。

1.2 新旧单位的关系

理解 CSS 新单位时,可以先把它分成几个家族:

家族旧单位新单位核心变化
视口单位vw、vh、vmin、vmaxsvh、lvh、dvh、svi、lvi、dvi 等区分小视口、大视口、动态视口
容器查询单位cqw、cqh、cqi、cqb、cqmin、cqmax相对最近的容器尺寸计算
字体相对单位em、rem、exch、ic、cap、lh、rlh更细粒度地描述字符和行盒子
绝对单位px、mm、cmq四分之一毫米,印刷场景用

可以看到,新单位不是把旧单位推翻,而是在旧单位的基础上增加“更精确的参考系”。比如vh只有一个固定参考系,而svhlvhdvh分别对应小视口、大视口和动态视口,提供了三种不同视角的高度参考。

2. 环境准备与兼容性说明

2.1 本文示例的运行环境

本文的代码示例以“现代浏览器”为前提,建议在下面环境中运行:

  • 操作系统:Windows / macOS / Linux 均可
  • 浏览器:Chrome、Edge、Firefox、Safari 的较新版本
  • 本地开发:VS Code + Live Server,或者任意静态服务器
  • 构建工具:本文示例是纯 HTML/CSS,不需要额外构建,复制到.html文件即可预览

需要特别说明的是,CSS 新单位的浏览器兼容性差别很大。像dvhsvh这类视口单位已经在多数现代浏览器中得到支持,但cqicqw这类容器查询单位虽然逐步铺开,仍有版本差异。更冷门的capicrlh则需要仔细确认目标用户使用的浏览器版本。

2.2 兼容性查询方法

在项目里使用一个 CSS 新单位之前,我最推荐的做法是打开 Can I Use 网站查一下。以dvh为例:

https://caniuse.com/viewport-unit-variants

如果某个单位在你的目标浏览器中支持率不理想,就按下面两个方向处理:

  1. 先写一个常用的兜底值,再写新单位,让浏览器优先使用它认识的新单位;
  2. 如果新单位只是增强效果,而不是关键功能,那就把它当作渐进增强,不要影响核心布局。

版本需要根据项目实际情况调整,本文重点演示的是配置思路和用法,不是某个固定版本下的绑定语法。

3. 新单位全景:先看看都有谁

这一节先把目前能见到的 CSS 新单位按类别过一遍。不需要立刻记住每一个的换算细节,先建立一个“有哪些、大致在什么场景用”的框架。

3.1 视口相关新单位

传统视口单位vh有一个常年被吐槽的问题:在移动端浏览器里,100vh不一定等于用户当前看到的视口高度,因为浏览器地址栏会占据一部分空间,而且会在滚动时收起。不同的浏览器对“视口高度”的定义也不同,这就导致100vh布局在部分场景下底部被遮挡,或者出现滚动跳动。

CSS Values and Units Module Level 4 引入了一组新的视口单位:

  • svh:小视口高度,表示浏览器工具栏全部展开时的高度;
  • lvh:大视口高度,表示浏览器工具栏全部收起时的高度;
  • dvh:动态视口高度,会随着工具栏状态实时变化;
  • svwlvwdvw:对应宽度方向的小视口、大视口、动态视口;
  • svilvidvisvblvbdvb:分别是内联方向和块方向的视口单位。

在这些单位中,dvhsvh是目前实用价值最高的。dvh适合绝大多数“撑满整个屏幕”的场景,因为它能跟随浏览器工具栏的变化自动调整;svh适合一些不希望因为工具栏状态变化而跳动的布局,比如底部的固定操作栏。

3.2 容器查询单位

容器查询是近几年 CSS 的重要能力之一。传统媒体查询@media只能根据视口宽度来改变样式,但实际开发中,一个组件放在侧边栏是 300px,放在主区域是 800px,我们希望组件能根据“父容器宽度”自适应,而不是被视口宽度绑架。

容器查询单位就是配合container-type属性使用的:

  • cqw:最近容器宽度的 1%;
  • cqh:最近容器高度的 1%;
  • cqi:最近容器内联方向尺寸的 1%;
  • cqb:最近容器块方向尺寸的 1%;
  • cqmincqicqb中较小的一个;
  • cqmaxcqicqb中较大的一个。

要启用容器查询,父元素需要设置container-type,常用值是inline-size,表示只让容器在水平方向参与查询。如果不设置这一条,子元素里的cqicqw单位是不会生效的。

3.3 字体排印类单位

字体相对单位原本是emrem的天下,但这两个单位在精细排版中并不够用。后来 CSS 补全了下面几个单位:

  • ch:字符 “0” 的宽度。这个单位适合控制等宽字段的宽度,例如验证码输入框、序列号展示框;
  • ic:CJK 表意字符的宽度,常用于中文排版场景;
  • cap:当前字体大写字母的高度,适合做标题或首字母相关的排版;
  • lh:当前元素line-height的计算值,适合做与文字行高对齐的样式;
  • rlh:根元素line-height的计算值,适合全站统一的行高基准。

其中lhrlh在最近的开发中比较常见。比如想要绘制一条与某行文字高度完全一致的背景条,直接使用height: 1lh会比拍脑袋写死px值更可靠。

3.4 其他容易被忽略的单位

除了上面几类,CSS 还定义了一些相对冷门的单位。比如q,全称是 quarter-millimeter,表示四分之一毫米,即1q = 0.25mm。它的主要应用场景是印刷样式,在屏幕端几乎用不到。

另外还有一些开发者容易混淆的“新能力”,比如min()max()clamp()round()mod()rem()这些数学函数。它们不是单位,但经常和新单位一起出现在讨论中。clamp()可以配合vwrem做自适应字号,例如:

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的间距就天然保持一致。

不过要注意,lhrlh在部分浏览器中的支持情况仍有差异,涉及关键布局时建议提供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不生效浏览器不支持相对行高单位empx兜底,并将新单位作为增强
写了新单位但整条样式被忽略拼写错误或浏览器不认识该单位打开浏览器控制台,查看样式是否被划掉

排查这类问题时,建议直接打开开发者工具,选中目标元素,看 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; /* 支持动态视口时使用 */ }

如果你的布局需要“尽量撑满屏幕且希望稳定”,用svhdvh更稳。如果希望跟随工具栏状态变化获得更多空间,就用dvh

5.4 单位混用导致计算异常

CSS 新单位之间并不总是可以直接混入calc()进行运算,这取决于浏览器对单位解析的支持程度。例如:

width: calc(100cqi - 32px);

这类写法在现代浏览器中已经能够工作,但在老旧浏览器中会被整体忽略。对于关键布局,建议先用百分比或px把主结构定好,再考虑用cqi做细节增强。

5.5 排查清单

遇到 CSS 新单位相关的疑难杂症时,可以按照下面的顺序排查:

  1. 检查单位拼写是否准确;
  2. 检查目标元素是否处于正确的“参考系”内(例如容器查询需要容器祖先);
  3. 打开开发者工具,确认样式是否被浏览器解析并生效;
  4. 查看兼容性列表,确认目标浏览器是否支持该单位;
  5. 先加一层传统的px%vh兜底;
  6. 用最小化复现法,把样式缩小到只有新单位的那一条,从复杂页面中抽离出来单独验证。

6. 最佳实践与工程建议

6.1 把“兼容性检查”放在第一位

新单位进入项目前,不要只看它能不能在本地 Chrome 里跑。最好看看产品的用户分布,尤其是移动端用户,是否集中在旧版本浏览器或老旧 WebView。如果核心布局严重依赖不兼容的新单位,宁可不用,也不要为了新技术冒险。

6.2 兜底写法是基本素养

任何新单位都应该有一条老单位作为兜底。通用的写法是“老值在前,新值在后”:

height: 100vh; height: 100dvh;

浏览器会优先选择它认识的后一条声明,不认识的则跳过。这样既不影响老浏览器,也能让新浏览器获得更好的表现。

6.3 用 CSS 变量封装新单位

团队协作时,直接把cqisvh散落在各处,很容易让其他同事看不懂。建议把关键的新单位封装成有语义的 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 只在渐进增强场景中使用

新单位更适合作为“渐进增强”的手段,而不是唯一的布局依赖。比如:

  • 核心布局先用flexgrid%vh搭好;
  • 需要用新单位微调的细节,例如底部高度、字体大小、间距,再基于新单位覆盖。

这样即使浏览器不支持新单位,页面也不会出现结构性坍塌。

6.5 团队协作时留注释

新单位终究不像px%那样人人熟悉。在代码里写清楚为什么用这个单位,能减少后续维护成本:

/* * 使用 svh 作为兜底: * 当浏览器支持 dvh 时,动态高度体验更佳; * 不支持时,svh 可以稳定地占满小视口。 */ .page { height: 100svh; height: 100dvh; }

一段清晰的注释,往往比文档中心还能更快地帮助同事理解你的设计意图。

6.6 关注规范更新,但别追新内卷

CSS 相关规范仍在持续演进,未来可能还会有新的单位出现。建议保持关注,但不要为了“用新单位”而用。一个单位是否进入项目,应该取决于它能否解决实际问题,而不是因为它符合“新技术潮流”。

7. 总结与下一步

CSS 新单位并不是一个孤立的知识点,它背后是视口适配、容器查询、字体排版规范这几个方向的同步演化。从dvh解决移动端高度抖动,到cqicqw让组件级响应式成为可能,再到lhch带来的排印控制力,真正成熟的单位进入项目后,能明显减少硬编码和魔法数值。

下一步建议你动手试试这几件事:

  1. 打开一个移动端项目,把某处的100vh改写成100svh+100dvh的兜底组合,观察地址栏收起和展开时的表现;
  2. 把一个卡片组件的父容器加上container-type: inline-size,用cqi替代部分媒体查询;
  3. lh写一次消息列表的状态点,感受行高对齐的便利。

如果你也想在评论区分享自己的使用经验,或者遇到了其他奇怪的新单位问题,欢迎留言讨论。这篇文章里的示例代码我放在本地一个demo文件下,后边有需要可以直接复制改造成自己的场景。如果觉得这篇梳理有用,可以收藏备用,等真正遇到对应需求时再拿出来对照。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询