结论:绝大多数项目可以用「字体和间距用 rem,组件内部用 em,视口相关的自适应交给 vw 配合 clamp(),边框和 1px 细节用 px」这一组合。它们不是互相替代的关系,而是各自解决不同问题;混用并非错误,但需要先明确每个单位在当前代码中跟随什么变化。
rem、em、vw 到底有什么区别
三者的核心差异在于参照物不同:rem 始终锚定根元素 <html> 的 font-size,em 锚定当前元素自身的 font-size,vw 锚定视口宽度(1vw 等于视口宽度的 1%)。参照物不同,决定了它们在嵌套结构和视口变化下的表现完全不同。
1rem 在默认情况下等于 16px,只有 html 的 font-size 变化时才会变;改任何子元素字号都不影响它。
1em 的值会随父级字号传递。父级设 font-size: 20px,子元素 padding: 1em 就是 20px;如果子元素再改字号,孙元素的 em 又跟着变。
1vw 在 375px 宽的屏幕上等于 3.75px,在 1440px 屏幕上等于 14.4px,与字号完全无关。
结论:rem 适合需要全局统一比例的场景,em 适合需要跟随局部字号缩放的场景,vw 适合需要跟随屏幕宽度缩放的场景。
什么时候该用 rem
rem 最适合用于页面级字号、组件间距、圆角和容器最大宽度,因为这些值在全站应保持统一尺度。
实践中常见两套根字号方案:
/* 方案一:1rem = 10px,方便口算 2.4rem = 24px */
html { font-size: 62.5%; } /* 16px × 62.5% = 10px */
.card { padding: 2rem; font-size: 1.4rem; }
/* 方案二:移动端按设计稿动态算根字号,375 设计稿下 1rem = 100px */
html { font-size: calc(100vw / 3.75); }
.card { padding: 0.16rem; font-size: 0.14rem; }
方案二的缺点是根字号被 vw 撑得过大,在大屏上会失控,必须配合 clamp() 限制上下限:
html { font-size: clamp(50px, 26.67vw, 100px); }
注意一个常见错误:不要为了让 1rem 等于 10px 就在 html 上写 font-size: 10px,这会覆盖用户的浏览器默认字号设置,破坏无障碍缩放。使用 62.5% 或 clamp() 相对值才安全。
em 该用在哪,问题在哪
em 的价值在于组件级等比缩放:按钮的 padding、图标尺寸、行内标签的间距,用 em 写一次,外层改字号整个组件就跟着缩放,不需要逐条改数值。
.btn { font-size: 16px; padding: 0.5em 1em; border-radius: 0.25em; }
.btn--large { font-size: 24px; } /* padding 自动变成 12px 24px */
问题在于嵌套叠加。三层都写 font-size: 0.8em,最终字号是 16 × 0.8³ ≈ 8.2px,而不是 12.8px。结论:em 只用于“确实需要跟随最近一层字号”的单层场景,字号本身一律用 rem 或 px 定义,避免层层相乘。
另外,行高永远用无单位数字(line-height: 1.5),不要写成 line-height: 1.5em,因为后者会按元素自身字号计算,并与继承行为相互干扰。
vw 怎么用才不容易出问题
vw 最常见的用途是让字号、间距随屏幕宽度平滑变化,标准写法是 clamp():
h1 { font-size: clamp(24px, 4vw, 40px); } /* 24px 下限,40px 上限 */
.section { padding: clamp(16px, 5vw, 64px) 0; }
有两个问题必须提前处理:
100vw 不等于可用宽度。桌面端出现纵向滚动条时,width: 100vw 会超出内容区并产生横向滚动条。解决办法是用 width: 100%,或 width: calc(100vw - var(--scrollbar-width)),或给 html 加上 scrollbar-gutter: stable。
- 移动端
100vh 不准。iOS Safari 地址栏收起/展开会改变视口高度,导致全屏区块跳动。改用 100dvh(动态视口高度),需要兼容旧浏览器时用 100svh 兜底。
还有一个边界:不要用纯 vw 做正文字号,例如 font-size: 3vw。用户在浏览器里调大默认字号时,vw 文本不会响应,这属于无障碍缺陷;用 clamp() 把 px 作为下限就能避免。
实际项目怎么搭配
结论:先按“这个值应该跟随什么变化”来选单位,不要按个人编码习惯来选。
| 用途 | 推荐单位 | 说明 |
|---|
| 正文字号、标题字号 | rem + clamp() | 跟随用户默认字号设置 |
| 组件内 padding、图标尺寸 | em | 跟随组件自身字号缩放 |
| 页面级间距、栅格 gutter | rem | 全站统一比例 |
| 1px 边框、细线 | px | 不应随缩放变粗 |
| 全屏高度 | dvh / svh | 规避移动端视口跳动 |
| 容器宽度 | % + max-width | 避免 100vw 溢出 |
| 行高 | 无单位数字 | 如 1.5、1.75 |
最后补充一条实操建议:新项目直接使用 rem + clamp() + dvh 这套组合,em 只保留给需要等比缩放的 UI 组件;老项目迁移时不要全局替换单位,优先改字号和断点相关代码,边框和 1px 细节保留 px 更稳定。