前端知库

rem、em、vw 有什么区别?怎么换算?

rem 相对根元素 html 的字号,em 相对当前元素的字号,写在 font-size 上时相对父元素字号,嵌套会层层叠加。1vw 等于视口宽度的 1%,和字号无关。换算公式是 px = 倍数 × 参照值,比如根字号 16px 时 1.5rem 是 24px,375px 宽的视口里 1vw 是 3.75px。

参考答案

简答

三个单位都是“倍数乘以参照值”,区别在参照谁。rem 参照根元素 html 的字号,em 参照当前元素自己的字号,vw 参照视口宽度,1vw 是视口宽度的 1%。

换算就是乘法。浏览器默认根字号通常是 16px,这时 1.5rem 是 24px;一个字号 20px 的元素上,padding: 1em 就是 20px;视口 375px 宽时,20vw 是 75px。

em 最容易算错。写在 font-size 上时,它参照的是父元素的字号,所以一层套一层都写 1.5em,字号会变成 1.5 倍、2.25 倍地往上叠。rem 始终参照根元素,不会叠加,所以全局字号和间距一般用 rem,只有希望跟着组件字号一起缩放的尺寸才用 em。

vw 和字号无关,适合按屏幕宽度等比缩放的布局。但纯用 vw 写字号,用户调整浏览器字号设置时文字不会变,一般配合 clamp() 和 rem 一起用。

三个单位分别参照谁

MDN 对这几个单位的定义是:rem 表示根元素(通常是 <html>)的字号;em 表示元素计算后的字号,用在 font-size 属性本身时表示继承来的字号,也就是父元素的字号;vw 等于初始包含块宽度的 1%,初始包含块的尺寸来自视口。

下面这段 CSS 中,外层容器字号是 16px,里面两层都写 1.5em,另一个元素写 1.5rem。

CSS
.parent { font-size: 16px; }
.nested { font-size: 1.5em; }
.root-based { font-size: 1.5rem; }

用 headless Chrome 实测,在根字号为 16px 的页面里,.nested 套两层后字号是 36px,即 16 × 1.5 × 1.5;.root-based 不管套在哪里都是 24px。这就是 em 会叠加、rem 不会的原因:每一层的 em 都以父元素算好的字号为基准。

同一个元素上,font-size 以外的属性写 em,参照的是这个元素自己的字号。实测一个 font-size: 20px; width: 10em 的元素,宽度是 200px。按钮的内边距用 em,按钮字号变大,内边距跟着变大,就是利用这一点。

怎么换算

公式都是 px = 数值 × 参照值。反过来,已知设计稿的 px 要换成 rem,就除以根字号;要换成 vw,就是 px ÷ 设计稿宽度 × 100。比如 375px 宽的设计稿上 75px 的按钮,写成 75 / 375 × 100 = 20vw。

有一种老写法是把根字号设为 62.5%,16px 的 62.5% 是 10px,这样 1.5rem 就是 15px,换算方便。实测在 Chrome 里设了以后,1.5rem 计算出来正是 15px。它基于百分比,用户把浏览器默认字号调大时会一起变大,比直接写 html { font-size: 10px } 好。

移动端用 rem 或 vw 做整屏等比缩放的完整方案,见《移动端怎样做屏幕适配?1px 边框为什么看起来变粗了?》;svh、dvh 等视口高度单位和百分比的参照规则,见《px、rem、em、百分比和动态视口单位怎么选?》。

vw 有哪些要注意的地方

vw 参照的是视口,不是父元素。一个 width: 50vw 的元素放进 300px 宽的侧栏里,宽度仍然是视口的一半,会撑出侧栏。要相对父元素就用百分比,要相对组件容器可以用容器查询单位 cqw。

字号全部用 vw,大屏上会过大、小屏上会过小,也不会响应用户的字号设置。常见写法是 font-size: clamp(1rem, 0.875rem + 0.5vw, 1.25rem),用 rem 设上下限,中间按视口宽度变化。

常见追问

子元素继承的是 em 还是算好的 px?

继承的是计算值。MDN 说明子元素不会继承父元素写的相对值,而是继承算出来的值。所以父元素 font-size: 1.5em 算出 24px,子元素继承到的就是 24px,子元素再写 1.5em 才会在 24px 的基础上乘。

rem 的根字号可以用媒体查询改吗?

可以。在媒体查询里改 html 的 font-size,所有 rem 尺寸会一起变。不过要用百分比或 rem 这类相对值来改,保留用户在浏览器里设置的默认字号。