ARTICLE · INTELLIGENCE

战地情报 · 详情页

来自尧图项目组的一线实战观察与深度解析

CSS新单位实战:dvh、容器查询与fr如何重构响应式布局

CSS新单位实战:dvh、容器查询与fr如何重构响应式布局 CSS 这几年在单位上的更新密度比很多前端框架的发版频率还高。很多人日常工作还停在 px、rem、vw/vh 老三样打开 MDN 却突然发现侧边栏多了一串字母dvh、svh、lvh、cqw、cqi、cqb、cqmin、cqmax、vi、vb、lh、rlh、cap、ic……一时间有点“CSS 是不是要改朝换代”的错觉。这篇文章不打算按单位全集从头背到尾只做一件事从这一大批 CSS 新单位里“屎里淘金”把真正能解决实际布局问题的几个挑出来讲清楚它们适合什么场景、怎么写、怎么降级、怎么排查。如果你平时做响应式布局、移动端适配或者正在维护一个组件库这篇文章可以直接收藏。先说几个值得现在就关注的单位dvh 系列是移动端 100vh 难题的正解容器查询单位 cqi、cqw、cqmax 等是组件级响应式的关键拼图逻辑视口单位 vi、vb 在多语言和竖排场景下很有价值lh、rlh、cap、ic 这些排版单位则适合做精细的字体对齐。它们不是学术概念而是可以直接写进生产代码的能力。本文会带你先过一遍新单位地图然后逐个实测核心单位最后用一个移动端自适应卡片网格把 dvh、容器查询单位、vi 组合起来跑通。看完你至少能判断哪些新单位值得给团队引入哪些只需要“认识一下”。1. CSS 新单位核心能力速览先给出一张速览表。这张表不追求覆盖所有单位只列出实际项目里出现频率较高、或者概念上容易混淆的 CSS 新单位。单位含义相对基准典型使用场景dvh / svh / lvh动态 / 小 / 大视口高度视口高度移动端全屏布局、地址栏伸缩适配dvw / svw / lvw动态 / 小 / 大视口宽度视口宽度横屏页面、侧边栏安全区cqw / cqh容器查询宽度 / 高度最近查询容器组件级响应式cqi / cqb容器查询 inline / block 尺寸查询容器的逻辑尺寸多语言、竖排组件适配cqmin / cqmax容器查询尺寸较小 / 较大值查询容器逻辑尺寸卡片等比缩放vi / vb视口逻辑尺寸根元素书写模式的视口方向国际化排版、竖排文字lh / rlh行高 / 根元素行高当前元素 / 根元素行高排版间距与元素对齐cap / ic / ch / ex字体度量单位字体字形尺寸表单、中文排版、字号精确控制fr剩余空间比例grid 容器剩余空间Grid 布局等分列从表格可以看出来新单位不是凭空出现的它们的核心逻辑是“把过去只能靠 JS 测量或写死像素的场景变成 CSS 自身的相对长度”。其中 dvh 和容器查询单位属于“直接解决痛点”的类型vi/vb 属于“为规范补全而存在”的类型lh/cap/ic 则偏向专业排版。实际开发中不需要全部记忆优先理解视口动态单位和容器查询单位就能覆盖大多数需求。2. 为什么 CSS 需要这么多新单位传统布局单位不是不够用而是很多“需要相对某个容器”的诉求一直没有一个原生单位来承接。旧方案里子元素想按父容器宽度等比缩放要么靠百分比要么用 aspect-ratio要么用 JS 监听 resize 再改 CSS 变量。百分比只能相对父级宽度高度则经常失效视口单位虽然好用但只能相对整个视口。当页面结构复杂到组件库层面“这个卡片要在不同宽度容器里自动缩放”就成了刚需浏览器厂商于是把容器查询和配套单位实现了出来。另一个驱动因素是移动端视口的“动态”变化。100vh 在移动浏览器里是一个被吐槽了很多年的写法地址栏出现和收起会导致实际可见高度变化但 100vh 的值并不总是动态跟随结果就是底部内容被遮住或者页面出现一条空白。早期做法是用 window.innerHeight 配合 JS 去同步后来又有人用 aspect-ratio 和 position: fixed 绕过。但这些都是 workaround。dvh、svh、lvh 的出现让 CSS 终于能直接表达“当前可用的视口高度”、“最小可用高度”和“最大可用高度”这是设计层面的补全。还有一个容易被忽略的领域逻辑属性。中文、英文基本是横向排版的所以 vw/vh 就够了但很多 CSS 新特性要求支持竖排、RTL 或混合排版。vi/vb 这种基于 inline 和 block 方向的单位能在不感知具体书写方向的情况下完成布局。类似的还有容器查询单位里的 cqi/cqb。这些单位看起来数量多其实是“物理方向单位”和“逻辑方向单位”的组合产物。如果算上小、大、动态三档视口变体轻松就能凑出十几个单位。这也是为什么标题会说“一不留神蹦出好几十个”。3. 环境准备与前置条件CSS 新单位不需要安装任何依赖也不需要构建工具。准备一个浏览器和一个编辑器就能开始验证。建议使用 Chrome 或 Edge 的稳定版因为容器查询单位和动态视口单位在 Chromium 内核中支持得比较早Safari 和 Firefox 近几年也陆续跟进。具体支持到什么版本直接查 caniuse 是最稳的不同浏览器对 cqi、dvh、vi 的支持进度并不一致。虽然不用安装但建议准备一个本地 HTML 测试页把本文里的代码粘进去直接跑。测试页只需要一个简单的 HTML 骨架外部引入一个 style.css 或者直接用内联 style 都可以。下面的代码可以用来做特性检测在不支持新单位的环境里提前给用户提示。// 在浏览器控制台运行检查当前环境是否支持关键新单位 console.log(dvh:, CSS.supports(height, 100dvh)); console.log(cqi:, CSS.supports(width, 1cqi)); console.log(vi:, CSS.supports(width, 90vi)); console.log(lh:, CSS.supports(margin, 1lh));如果你维护的是移动端 H5推荐用 Chrome DevTools 的设备模拟器勾选“显示设备工具栏”后切换为 iPhone 或 Android 机型动态观察 dvh 和 svh 在地址栏伸缩时的差异。需要注意特性检测通过不代表效果一定符合预期尤其是 iOS Safari 上动态视口单位的表现必须以真机测试为准。测试环境建议准备一台 iOS 和一台 Android 设备这是移动端新单位最容易踩坑的地方。4. 实用新单位逐个拆解与测试4.1 dvh / svh / lvh移动端视口高度单位先看最值得用的一个系dvh、svh、lvh。它们的含义分别是动态视口高度、小视口高度和大视口高度。移动端浏览器顶部地址栏和底部导航条是动态元素当它们展开时可见视口高度变小收起时变大。svh 表示最小可见视口高度lvh 表示最大可见视口高度dvh 则是当前实际高度会跟随浏览器 UI 变化实时更新。在实际项目中dvh 最常见的用法是替换 100vh。比如一个全屏落地页旧写法是div classhero主视觉区域/div.hero { height: 100vh; /* 旧写法移动端可能溢出 */ height: 100dvh; /* 新写法值会随视口变化 */ }当你同时写两行 height 时不支持 dvh 的浏览器会忽略第二行回退到 100vh支持 dvh 的浏览器则使用动态视口高度。这个降级技巧是 CSS 特性检测最简单的形式适用于所有新单位。测试时可以把滚动条拉到底然后在移动端模拟地址栏展开和收起观察元素高度是否跟着变化。如果高度没有变化说明当前浏览器把 dvh 当作固定高度处理了或者页面存在更上层的高度约束。另一个容易混用的点是 svh如果希望内容不被地址栏遮挡同时不想出现大幅跳动可以用 min-height: 100svh 作为保底。这样可以保证即使地址栏占满内容依然处于安全可见范围内。4.2 cqw / cqh容器查询宽高单位容器查询单位是目前最值得工程化引入的一类新单位。cqw 和 cqh 分别表示最近查询容器宽度的 1% 和查询容器高度的 1%。要使用它们父元素必须先声明容器属性。.card-container { container-type: inline-size; container-name: card; } .card { padding: 2cqw; margin: 1cqw; width: 30cqw; }这里 container-type: inline-size 表示容器只响应内联方向尺寸变化也就是宽度。cqi 通常比 cqw 更推荐因为它考虑了书写模式。但在中文横排场景下cqi 和 cqw 表现一致选择其中一个保持一致即可。容器查询单位的核心意义在于组件不再依赖全局视口宽度做响应而是根据自己父容器的尺寸变化。比如同一个卡片组件放在侧边栏时是窄布局放在主内容区时是宽布局用容器查询单位和 cqi 就能自动适配不需要 Media Query 判断全局宽度。测试时最简单的方式是拖拽父容器宽度观察子元素是否有按比例缩放的效果。如果 cqi 无效优先检查父元素是否设置了 container-type这是最容易被遗漏的前提。4.3 cqi / cqb 与逻辑方向cqi 和 cqb 与 cqw/cqh 的区别在于参照方向。cqi 是查询容器 inline size 的 1%inline 方向在横向排版中就是水平方向cqb 是 block size 的 1%在横向排版中就是垂直方向。对于大多数中文和英文项目cqi 可以理解成宽度百分比cqb 可以理解成高度百分比。但它们真正的价值在竖排和 RTL 场景。假设一个容器使用 writing-mode: vertical-rl那么 inline 方向变成了垂直此时 cqi 会随容器高度变化而不是宽度。如果你的组件需要同时适配横排和竖排使用 cqi/cqb 比 cqw/cqh 更语义化。.vertical-container { writing-mode: vertical-rl; container-type: inline-size; } .text { font-size: 3cqi; block-size: 80cqb; }这种写法不需要关心用户最终用的是哪种书写方向。注意block-size 和 inline-size 是逻辑属性与 cqb/cqi 配合后才能真正实现“一个组件适配多语言方向”。4.4 cqmin / cqmax等比缩放的便捷单位cqmin 和 cqmax 分别表示查询容器内联尺寸和块尺寸中较小值和较大值的 1%。这两个单位很适合做“正方形”或“固定比例”元素。假设有一个头像卡片要求尺寸始终是容器的 50%并且保持正方形.avatar-wrapper { container-type: inline-size; container-type: size; /* 需要 block 方向也参与尺寸计算 */ } .avatar { width: 50cqmin; height: 50cqmin; }这里 cqmin 取的是容器宽高中的较小值所以无论容器变宽还是变高头像都能自适应成正方形。容器查询容器的 container-type 需要设置为 size而不是 inline-size因为这里需要同时感知宽高。如果容器没有设置 container-typecqmin 不会生效。在 grid 或 flex 布局中cqmin 也可以用来控制文字和图标尺寸让它们始终跟随容器缩放。4.5 vi / vb视口的逻辑单位vi 是视口内联尺寸的 1%vb 是视口块尺寸的 1%。它们与 vw/vh 的关系类似于 cqi/cqb 和 cqw/cqh 的关系。vi 定义在根元素书写模式上而 vw 始终是物理宽度。在横排中文页面里 vi 基本等于 vw在竖排或 RTL 页面里才会有明显差异。vi 和 vb 适合用于页面级布局中的滚动容器或全屏强调元素。比如在横屏游戏页面希望元素高度占满视口宽度方向时使用 visual viewport 系的单位会更自然。日常横排项目里不必特意追求 vi/vb但如果你在做多语言支持或者需要给未来竖排布局留余地vi/vb 是比 vw/vh 更具前瞻性的选择。4.6 lh / rlh / cap / ic排版向单位lh 代表当前元素行高的 1 倍rlh 代表根元素行高的 1 倍。这类单位让 CSS 可以把边距、内边距和行高绑定。以前想实现“行高一致的分隔线”需要手工计算 px现在可以直接写成.divider { margin-top: 1lh; margin-bottom: 1lh; height: 1px; background: #e5e7eb; }如果字号和行高变化间距会同步变化不会出现“换了一个字号间距就崩了”的问题。cap 代表字体大写字母的顶线到基线的距离适合精确定位不需要内容区高度的元素。ic 代表中文字符“水”的宽度在做中文输入框、字数对齐时非常合适。一个典型场景是让中文文本缩进两个字符.paragraph { text-indent: 2ic; }这在支持 ic 的浏览器里比写 font-size * 2 更准确因为 ic 直接关联当前字体的中文字符宽度。但这些排版单位并不适合所有项目如果目标用户主要使用老浏览器优先用 rem 或 px 做兜底。4.7 frGrid 网格布局的比例单位严格来说 fr 不算“新单位”它随着 Grid 布局一起进入 CSS但很多从 Flex 转到 Grid 的开发者仍然不熟悉它。1fr 表示网格容器剩余可用空间的一份fr 是一种弹性比例单位。.grid { display: grid; grid-template-columns: 1fr 2fr 1fr; }这个写法会生成三列中间列是两侧的两倍宽。fr 单位与百分比不同它不会因 padding 和 border 撑破容器而是基于剩余空间计算。在响应式布局中fr 和 minmax、auto 结合非常实用.grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(min(100%, 280px), 1fr)); }这段代码会让网格列自动填充当容器宽度小于 280px 时列宽收缩而不是直接溢出。fr 是新单位时代的重要组成值得在每个 Grid 布局里优先考虑。5. 综合实操移动端自适应卡片网格下面把前面提到的新单位组合到一个完整案例里。目标是一个移动端优先的卡片网格页面整体高度使用 dvh 保证全屏卡片内部使用容器查询单位和 cqi 实现组件级响应式同时用 Grid 的 fr 单位控制列数。先看 HTML 结构section classpage div classgrid div classcard h2 classcard-title卡片标题/h2 p classcard-content卡片内容支持容器查询单位自动缩放/p /div div classcard h2 classcard-title第二个卡片/h2 p classcard-content容器宽度变化时字体和间距会跟着调整/p /div /div /section再看 CSS.page { display: flex; align-items: center; justify-content: center; min-height: 100dvh; padding: 4vi; box-sizing: border-box; } .grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(min(100%, 30cqi), 1fr)); gap: 2cqi; width: 100%; max-width: 1200px; } .card { container-type: inline-size; container-name: card; padding: 2cqi; border-radius: 1cqi; background: #f7f7f8; } .card-title { font-size: clamp(1.2rem, 2cqi 0.6rem, 2rem); } .card-content { font-size: clamp(0.9rem, 1cqi 0.4rem, 1.2rem); line-height: 1.6; }这里面出现了三个新单位dvh 保证页面最少占满一屏且随地址栏变化cqi 用于卡片内的字体和间距缩放fr 用于 Grid 自动分配列宽。实际运行效果是当卡片容器变宽时标题和内容字号变大间距变大当容器变窄时一切同步收缩。这比用 Media Query 逐级断点要平滑得多。如果你在本地测试可以直接用浏览器 DevTools 把页面宽度从 320px 拖到 1200px观察卡片内部元素的动态变化。重点观察卡片在变宽时标题字号是否连续缩放而不是跳变。如果不生效先检查 .card 是否设置了 container-type再检查 grid 列模板中 cqi 是否写在了容器查询容器之外。这个案例验证完成后你就掌握了 dvi、cqi、fr 三种新单位的基本组合方式。6. 其他你只需要“认识一下”的单位除了上面重点测试的单位CSS 新单位里还有一批“存在感较低、但特定场景能救命”的成员。svh 和 lvh 虽然没有 dvh 热门但作为 fallback 很有价值。svh 代表最小可见视口高度当你希望“在地址栏展开时内容也不会被遮挡”可以给元素同时设置 height: 100svh 和 height: 100dvh。lvh 代表最大视口高度更适合做“地址栏收起后依然完整覆盖”的全屏容器。这三个高度单位经常一起出现优先写 dvh再写 svh 做兜底即可。dvw、svw、lvw 是宽度方向的对应版本在移动端横屏、或浏览器侧边栏动态展开时使用。因为移动端宽度方向很少被地址栏影响dvw 的实用频率远低于 dvh。但如果你在做桌面端可折叠侧边栏响应式布局dvw 也能提供一些灵感。cqw 和 cqh 在没有书写方向差异时可视为 cqi/cqb 的简化版。部分旧资料会把 cqw 挂在嘴边上其实是没把逻辑方向纳入考虑。新项目建议优先使用 cqi/cqb减少未来适配竖排或 RTL 的重构成本。ch、ex、cap、ic 这些字体度量单位属于“排版细节控”专用。ch 是数字 0 的宽度适合做等宽数据列ex 是 x 字高适合做基线对齐cap 用于大写字母高度ic 用于中文宽度。如果你不处理复杂的报表或多语言文字排版这些单位可以先不学不影响日常开发。此外CSS Values 4 还引入了 sin()、cos()、tan()、atan2() 等三角函数它们不是单位但会产生数值经常和单位混在一起用。比如用三角函数配合 CSS 变量实现波动动画或计算弧形路径上的坐标。考虑到这批函数需要配合 calc() 才能发挥价值且浏览器支持还在完善中本文不展开只需知道它们的存在即可。fr 在 Grid 里的地位也已经非常稳固不属于“新单位”但值得重新重视。如果你还在用百分比或 calc() 控制 Grid 列宽建议尽早切换到 fr 组合 minmax因为 fr 会自动吸收剩余空间不容易出现容器撑破问题。7. 常见问题与兼容性排查CSS 新单位在开发中踩坑多数不是单位本身的问题而是上下文条件不满足或者浏览器兼容差异。下面这张表格整理了几个高频问题。问题现象可能原因排查方式解决方案dvh 写上去没有变化浏览器版本较老、iOS Safari 特性检测通过但行为不一致使用 CSS.supports 检测在真机查看同时写 100vh 100dvh以 dvh 覆盖cqi 等容器查询单位不生效父级没有设置 container-type检查容器是否包含 container-type: inline-size给直接父元素补上 container-typecqmin 不生效容器只启用了 inline-size高度不参与查询查看容器是否设置 container-type: size改为 container-type: sizevi/vb 在中文页面看不出效果中文横排时 vi≈vw表现一致对比竖排 writing-mode 下的表现无需处理若需差异切换书写模式测试100dvh 在 iOS 键盘弹起时高度异常浏览器对键盘视口的处理不同用真机测试键盘弹起使用 svh 作为兜底或改用 position: fixedGrid 中 fr 和 minmax 造成列宽溢出容器内有最小内容宽度过大的元素检查子元素的 min-width给子元素设置 min-width: 0新单位在构建工具中被打包后语法报错构建工具或 PostCSS 插件不支持新语法查看编译后的 CSS 是否原样输出升级 PostCSS 或关闭相关插件这里重点解释两个常见陷阱。第一个是容器查询单位必须在“查询容器”内部使用如果父级没有设置 container-type那么 cqi 会被视为无效值整个声明失效。第二个是 dvh 在 iOS 上行为不稳定虽然 caniuse 标记支持但在键盘弹起、滚动地址栏时仍可能出现与预期不符的情况。因此生产环境建议始终采用“旧单位 新单位”的降级写法让不支持或行为异常的浏览器自然回退。另外特性检测的时机也很重要。CSS.supports 只能检测语法是否被支持不能检测行为是否完全一致。比如某些浏览器支持 100dvh 但不支持容器查询或者支持 cqi 但不支持 cqb。所以在排查问题时不要只看一种单位最好把使用到的每个单位都单独检测一遍。8. 最佳实践与使用建议针对 CSS 新单位核心建议是“降级优先、按需引入、组件内封闭”。降级优先指所有新单位都写成 fallback 形式。比如旧值在前、新值在后让不支持的浏览器自然忽略。容器查询单位则要配合 container-type 提供回退样式比如在容器查询单位不生效时保留一个基于 rem 的默认字号。按需引入指不要把新单位当作炫技工具。dvh 适合移动端全屏容器查询单位适合组件库vi/vb 适合多语言lh/rlh 适合排版系统。如果你的项目只是简单营销页继续用 px 和 rem 没有任何问题。新单位的价值在于减少 JS 和 Media Query 的维护成本而不是取代旧的单位体系。组件内封闭指容器查询单位的使用范围尽量控制在组件内部。不要在页面根元素上启动 container-type: inline-size除非你确定不会影响其他组件。容器查询的嵌套深度也会影响性能连续多层容器查询可能导致布局计算量增加所以在结构上尽量让查询容器扁平化。给团队引入新单位时一定要先建立一张“支持矩阵”。例如确认项目目标浏览器支持 dvh、cqi、cqmax 后再在组件库中使用。建议在代码注释里标明依赖的浏览器版本防止后续同事无意间删除 fallback。同时可以用 CSS 自定义属性封装常用单位比如:root { --dvh-full: 100dvh; --card-pad: 2cqi; --gap-md: 1.5rem; }这样调整单位策略时只改一处也能在特性不满足时通过替换变量统一回退。批量编辑场景里尽量使用 CSS 变量而不是直接改每个选择器。比如需要把所有卡片的 padding 从 2cqi 调整为 3cqi直接改变量即可。这种做法在组件库规模扩大后节省的维护成本非常可观。9. 总结与下一步CSS 新单位里最值得立刻引入生产环境的是 dvh 和容器查询单位那一组。dvh 解决了移动端地址栏导致的全屏高度问题容器查询单位则让组件真正拥有“感知父容器尺寸”的能力。ai 在横排中文场景下与 vw 几乎没区别但它是为多语言和竖排准备的正确方向新项目建议逐步切换。fr 虽然不新但配合 Grid 能极大简化响应式布局的列宽控制。最容易踩的坑是容器查询单位必须在设置了 container-type 的容器内部使用以及 dvh 在 iOS 上的表现需要真机验证。第一次测试新单位时建议直接创建一个最小 HTML 文件把本文中的用例粘进去先用 CSS.supports 检查基础支持再逐项验证。当你确认这些单位在当前目标浏览器上稳定后再逐步替换项目中高维护成本的老写法。下一步可以关注两部分一是容器查询单位与 CSS 自定义属性的组合用它们做一套真正的组件级间距系统二是 CSS Values 4 中更进一步的数学函数和三角函数配合新单位能够实现更多动态布局。CSS 新单位不会让你一夜之间写出更华丽的页面但能让你在布局和适配这件事上少写很多 JS这才是它们真正的价值。
RELATED READING

延伸阅读

更多一线实战笔记与深度复盘,助您持续精进