CSS 基于视区的长度单位
进入主题之前, 先来了解一下现有的 CSS 长度单位, 可以分为绝对长度单位和相对长度单位.
绝对长度单位
px
现代显示器是由一颗颗像素组成的, 以我的显示器为例, 分辨率为 2560x1664, 也就是说这块显示器横向有 2560 个像素, 纵向有 1664 个像素, 显示器的像素我们称为物理像素.

CSS 中的像素 px
我们称为逻辑像素, 它并不总是和物理像素一一对应的, 同等尺寸下现代显示器的分辨率越来越高, 如果逻辑像素和物理像素一一对应的话, 那么同样大小的区域在高分辨率的显示器下就会越小, 如下图所示:

显示器有个指标 PPI, 越大表示同等尺寸下像素点越多
除了显示尺寸的问题外, 一个物理像素对应一个逻辑像素的显示效果是比较差的, 容易产生锯齿感. 所以现代操作系统都会提供缩放功能, 多个物理像素对应一个逻辑像素, 这样的话显示效果会比较细腻, 比如 macOS:

也就是说我这块显示器物理像素是 2560x1664, 逻辑像素是 1470x956, 所以 CSS 中 1px
约等于 1.74 个物理像素.
在浏览器 API 中, 我们可以通过 window.devicePixelRatio
获取到物理像素与逻辑像素的比值, 不过这个值似乎并不准确, 规范描述中这是个双精度浮点数, 而在 MacBook 下测试所有浏览器都返回整数, 似乎对该值做了向上取整的操作.
其他绝对长度单位
绝对长度单位只需要知道 px
就可以了, 其他单位要不使用非国际单位, 对于我们来说难以理解, 比如英寸 in
, 要不使用频率非常低, 比如毫米 mm
, 但最主要的是这些单位都可以用 px
来表示, 比如 1in === 96px
, 1mm === 3.78px
.
相对长度单位
相对于上层元素
单位 | 描述 |
---|---|
em | 当用在 font-size 时表示相对于父元素的 font-size , 其他 CSS 属性中使用时相对于自身的 font-size |
rem | 相对于根元素 html 的 font-size |
相对于字体
单位 | 描述 |
---|---|
ex | 小写字符 x 的高度 |
ch | 数字 0 的宽度 |
不同的字体设计存在差异, 所以 ex
和 ch
的大小是跟具体字体相关的.
相对于行高
单位 | 描述 |
---|---|
lh | 当前元素 line-height |
rlh | 根元素 html 的 line-height |
相对于视区(Viewport)
Viewport 指的是浏览器窗口中页面的可渲染区域, 也就是下图中的红框部分.

单位 | 描述 |
---|---|
vw | 1vw 表示 Viewport 宽度的 1% |
vh | 1vh 表示 Viewport 高度的 1% |
vmin | 1vmin 表示 Math.min(Viewport.width, Viewport.height) 的 1% |
vmax | 1vmax 表示 Math.max(Viewport.width, Viewport.height) 的 1% |
vi | 逻辑单位, 取值与 writing-mode 相关, 1vi 表示 Viewport 内联元素流动方向上长度的 1% |
vb | 逻辑单位, 取值与 writing-mode 相关, 1vb 表示 Viewport 块级元素流动方向上长度的 1% |
vi
和vb
相关的writing-mode
可以参考之前的文章 CSS 书写模式和逻辑属性
Viewport 的大小和浏览器窗口相关, 所以以上单位会随着浏览器窗口大小的改变而改变.
以上是现有的 CSS 长度单位, 在 CSS Values and Units Module Level 4 中给视区增加了状态, 那什么是视区状态以及为什么要增加状态呢?
相对于 Viewport 的长度单位(vw
/vh
/…)在桌面浏览器上表现很好, 但是在移动设备上却存在一些问题. 在移动设备上为了尽可能地显示更多的内容, 浏览器会将地址栏/工具栏等元素覆盖在 Viewport 上或者使 Viewport 移位, 如下图所示:

然后往下滚动的过程中地址栏/工具栏等元素会逐渐消失或者隐藏, Viewport 也逐渐完全展示, 可以使用移动设备访问这个例子. 这就导致了相对于 Viewport 的单位在移动端表现是不符合预期的.

为了解决这个问题, CSS 指定了 Viewport 的状态:
- 大视区(LV, Large Viewport): Viewport 的最大展示区域, 也就是导航栏/工具栏之类的浏览器元素收起的时候
- 小视区(SV, Small Viewport): Viewport 的最小展示区域, 也就是导航栏/工具栏之类的浏览器元素展开的时候
- 动态视区(DV, Dynamic Viewport): 始终是 Viewport 的展示区域, 也就是处于 Large Viewport 时等于 Large Viewport, 处于 Small Viewport 时等于 Small Viewport

同时基于以上的状态新增了 lvw
/lvh
/lvi
/lvb
/lvmin
/lvmax
/svw
/svh
/svi
/svb
/svmin
/svmax
/dvw
/dvh
/dvi
/dvb
/dvmin
/dvmax
18 个长度单位. 虽然数量很多, 但是可以根据以上状态分成三类:
大视区 lv*
单位 | 描述 |
---|---|
lvw | 1lvw 表示 Large Viewport 宽度的 1% |
lvh | 1lvh 表示 Large Viewport 高度的 1% |
lvmax | 1lvmax 表示 Math.max(LargeViewport.width, LargeViewport.height) 的 1% |
lvmin | 1lvmin 表示 Math.min(LargeViewport.width, LargeViewport.height) 的 1% |
lvi | 逻辑单位, 取值与 writing-mode 相关, 1lvi 表示 Large Viewport 内联元素流动方向上长度的 1% |
lvb | 逻辑单位, 取值与 writing-mode 相关, 1lvb 表示 Large Viewport 块级元素流动方向上长度的 1% |
小视区 sv*
单位 | 描述 |
---|---|
svw | 1svw 表示 Small Viewport 宽度的 1% |
svh | 1svh 表示 Small Viewport 高度的 1% |
svmax | 1svmax 表示 Math.max(SmallViewport.width, SmallViewport.height) 的 1% |
svmin | 1svmin 表示 Math.min(SmallViewport.width, SmallViewport.height) 的 1% |
svi | 逻辑单位, 取值与 writing-mode 相关, 1svi 表示 Small Viewport 内联元素流动方向上长度的 1% |
svb | 逻辑单位, 取值与 writing-mode 相关, 1svb 表示 Small Viewport 块级元素流动方向上长度的 1% |
动态视区 dv*
单位 | 描述 |
---|---|
dvw | 1dvw 表示 Dynamic Viewport 宽度的 1% |
dvh | 1svh 表示 Dynamic Viewport 高度的 1% |
dvmax | 1dvmax 表示 Math.max(DynamicViewport.width, DynamicViewport.height) 的 1% |
dvmin | 1dvmin 表示 Math.min(DynamicViewport.width, DynamicViewport.height) 的 1% |
dvi | 逻辑单位, 取值与 writing-mode 相关, 1dvi 表示 Dynamic Viewport 内联元素流动方向上长度的 1% |
dvb | 逻辑单位, 取值与 writing-mode 相关, 1dvb 表示 Dynamic Viewport 块级元素流动方向上长度的 1% |
lv*
/sv*
可能使用场景很少, 更多情况下还是使用 dv*
. 像一些移动端的页面我们为了撑满一屏会使用 height: 100vh
, 现在使用 height: 100dvh
会更符合预期.
最后就是兼容性问题, 目前从 Can I use 上看不太乐观, 生产环境下还是要避免使用.
