CSS 单位详解:px、rem、em、%,以及什么时候该用哪一个
2026-08-24
font-size: 16px 和 font-size: 1rem 可能渲染得一模一样——直到它们不一样。你选的单位不是口味,而是一条解析规则:它决定这个长度对什么求值,也就决定了浏览器缩放、根字号变化、父元素变化或视口变化时会发生什么。大多数”布局莫名其妙就坏了”的 CSS bug 其实是单位 bug。本文给你一张决策表,让你第一次就选对。
绝对与相对
每条 CSS 长度要么是绝对的,要么是相对的:
- 绝对单位(
px,以及面向打印的pt、cm、mm、in)是固定的。16px永远是 16 CSS 像素,不随任何东西缩放。 - 相对单位对别的东西求值:根字号(
rem)、元素自身字号(em)、父元素(%)、视口(vw、vh、dvh)、或字体度量(ch、ex)。
一旦缩放或布局变化,绝对单位原地不动,相对单位跟着动。这正是本文的工作——为每个属性决定它想要哪种行为。
px:设计基线
像素是设计单位:大多数设计稿以 px 交付,而 1px 是边框、发丝分隔线和盒阴影偏移的清晰度下限。
border: 1px solid #ddd; /* 永远是一条利落的线 */
box-shadow: 0 1px 2px rgba(0,0,0,0.1);
陷阱是把 px 用在文字上。font-size: 14px 无视用户的字号偏好和浏览器缩放设置——所以 px 文字是经典的可访问性投诉。经验法则:结构与效果用 px,文字用 rem。
rem:锚定在根上的标尺
rem 是”root em”:一个 rem 等于 <html> 元素的 font-size。改根值,文档里所有 rem 一起缩放:
html { font-size: 100%; } /* 默认 16px */
.title { font-size: 2rem; } /* 根为 16px 时是 32px */
.body { font-size: 1rem; } /* 16px */
因为锚定在一个根值上,rem 给你整文档标尺:调根值,整套排版系统一起缩放。又因为浏览器和用户偏好会设置这个根值,rem 文字尊重用户字号设置——这就是可访问性默认。现代 CSS reset 鼓励文字用 rem 的原因也在此。(两个单位随手换算,用 Px to Rem Converter。)
em:相对元素自身——而且会复利
em 相对元素自身的 font-size。用在元素自身上很优雅:按钮的 padding: 0.5em 会随它的文字一起缩放。
危险在于嵌套里的复利。em 对后代是按最近的祖先字号计算的,而祖先的 em 会相乘:
/* 每层都相乘:16px → 25.6px → 41px → 65px */
div { font-size: 1.6em; } /* 在 16px 父级里 → 25.6px */
div div { font-size: 1.6em; } /* 41px */
div div div { font-size: 1.6em; } /* 65px */
一条看起来是”1.6 倍”的规则,嵌套几层就变成”1.6ⁿ 倍”。em 很适合组件内部、应随该组件文字缩放的间距;用于全局字号标尺则有危险,那里 rem 更稳妥。
%:两条截然不同的规则
% 是最被滥用的单位,因为含义取决于属性:
- 在 font-size 上,
%相对继承的字号:font-size: 150%= 父级字号的 1.5 倍(与1.5em行为相同)。 - 在布局属性(width、margin、padding、top/left)上,
%相对包含块——而且宽度的百分比和高度百分比不一样(高度百分比在父级没有明确高度时常无法解析)。
.child { width: 50%; } /* 父级宽度的一半 */
.parent { font-size: 120%; } /* 祖父字号的 1.2 倍 */
所以 % 用在宽度/边距是流式布局的看家本领,用在 font-size 上实际就是 em——按你真正想表达的意思来选。
视口单位:vw、vh、dvh
视口单位跟随浏览器窗口:1vw = 视口宽度的 1%,1vh = 视口高度的 1%,vmin/vmax 是两者中较小/较大的那个。
.hero { height: 100vh; } /* 整个窗口高度 */
.fluid { font-size: clamp(1rem, 4vw, 2.5rem); } /* 流式字号,带钳制 */
两个实用提示:
100vh与手机。 手机上地址栏吃掉部分”视口”,所以100vh会超出一截被裁掉。dvh(动态视口高度)跟随可见视口,是现代解法:height: 100dvh。- 流式字号。
vw字号配clamp(),字号随屏宽缩放但被钳在界内——这是几条断点 hack 行的现代替代。
ch、ex 及其余
ch 等于当前字体里 0 这个字符的宽度——最适合等宽类尺寸:<code> 块上 width: 80ch 给出以字符计的舒适行长。ex 是字母 x 的高度,很少用。其余(cm、mm、in、pt)只在打印样式表里有意义。
选单位:一张决策表
| 你在给什么定尺寸 | 单位 | 为什么 |
|---|---|---|
| 文字(font-size) | rem | 尊重用户设置;随根缩放 |
| 边框、阴影、利落的线 | px | 任何缩放都固定清晰 |
| 组件内部、随组件文字缩放的内边距 | em | 随该组件缩放——但小心嵌套 |
| 宽度 / 流式布局 | %(或 flex/grid) | 跟随容器 |
| 整屏 hero、遮罩 | vw/dvh | 跟随可见视口 |
| 等宽行长 | ch | ”字符数”才是文本栏的正确单位 |
最干净的现代策略:类型用 rem、边框阴影用 px、布局用 % 或 grid/flex 轨道、em 只在自足组件内部用、任何要填满屏幕的东西用 dvh。
快速参考
- 绝对单位(
px、pt、cm、mm)固定;相对单位对父级、根或视口求值。 rem= 根字号——整文档标尺,尊重用户设置。文字默认。em= 元素自身字号;嵌套里复利(1.6ⁿ),组件内很棒,全局标尺有风险。%被过度使用:在 font-size 上是”× 继承字号”(同em);在 width/margin 上是”× 包含块”。1vw/1vh= 视口宽/高的 1%;手机整屏用dvh(地址栏问题)。ch=0的宽度;等宽行长最理想。- 策略:
rem类型 +px效果 +%/flex/grid 布局 +em组件内 +dvh整屏。
想把设计稿从 px 迁到 rem 又不想做算术,Px to Rem Converter 直接换算任意值并展示标尺。单位是每个布局、网格和渐变底下安静的底层——解析规则选对了,布局在每一个你从没测过的缩放档位上都站得住。