说说 em/px/rem/vh/vw 区别
问题解析
CSS 单位是样式的基础,不同单位有不同的计算基准和适用场景。面试考察这个问题,是想要了解候选人对响应式设计和可访问性的理解。
单位分类
| 类别 | 单位 |
|---|---|
| 绝对单位 | px, pt, cm, mm, in |
| 相对单位 | em, rem, %, vw, vh, vmin, vmax, ex, ch |
各单位详解
1. px(像素)
绝对单位,CSS 中最常用的单位。
.element {
width: 100px;
font-size: 16px;
}
特点:
- 精确控制,易于理解
- 与设备像素的关系受 DPR 影响
- 不利于响应式设计和可访问性
2. em
相对单位,相对于父元素的字体大小。
.parent {
font-size: 16px;
}
.child {
font-size: 1.5em; /* 16px × 1.5 = 24px */
padding: 2em; /* 24px × 2 = 48px(相对于自身字体大小) */
}
特点:
- 继承父元素字体大小
- 适合组件化开发
- 嵌套层级深时计算复杂
em 的复杂继承:
html { font-size: 16px; }
.level1 { font-size: 1.5em; } /* 24px */
.level2 { font-size: 1.5em; } /* 36px(24px × 1.5) */
.level3 { font-size: 1.5em; } /* 54px(36px × 1.5) */
3. rem(root em)
相对单位,相对于**根元素(html)**的字体大小。
html {
font-size: 16px;
}
.element {
font-size: 1.5rem; /* 16px × 1.5 = 24px */
padding: 2rem; /* 32px(始终基于 html 字体大小) */
}
特点:
- 计算基准统一,不层层继承
- 适合整体布局控制
- 是现代响应式开发的首选
** rem 方案**:
/* 基准设置 */
html {
font-size: 62.5%; /* 16px × 62.5% = 10px */
}
/* 使用 */
.element {
width: 30rem; /* 300px */
font-size: 1.4rem; /* 14px */
padding: 2rem; /* 20px */
}
4. vw(viewport width)
视口宽度单位,1vw = 视口宽度的 1%。
.element {
width: 50vw; /* 视口宽度的 50% */
font-size: 2vw; /* 视口宽度的 2% */
}
特点:
- 相对于视口,无视父元素
- 适合流体布局
- 字体大小随视口变化,可能过大或过小
5. vh(viewport height)
视口高度单位,1vh = 视口高度的 1%。
.hero {
height: 100vh; /* 视口高度的 100% */
min-height: 100vh; /* 确保至少一屏高 */
}
移动端 vh 问题:
移动端浏览器工具栏会占用视口高度,导致 100vh 可能超出可视区域。
解决方案:
/* 新 CSS 单位 */
.element {
height: 100dvh; /* 动态视口高度,推荐 */
}
/* 或 JS 方案 */
:root {
--vh: 1vh;
}
.element {
height: calc(var(--vh, 1vh) * 100);
}
window.addEventListener('resize', () => {
document.documentElement.style.setProperty('--vh', window.innerHeight * 0.01 + 'px');
});
单位对比
| 单位 | 相对基准 | 优点 | 缺点 | 适用场景 |
|---|---|---|---|---|
| px | 无(绝对) | 精确、可控 | 不灵活 | 边框、阴影等固定值 |
| em | 父元素字体 | 组件化 | 嵌套复杂 | 按钮内边距等 |
| rem | 根元素字体 | 统一、响应式 | 需要计算 | 布局、字体大小 |
| vw | 视口宽度 | 流体布局 | 可能失控 | 视口相关尺寸 |
| vh | 视口高度 | 全屏布局 | 移动端问题 | 全屏区块 |
深入理解
单位混合使用
.card {
/* 布局用 rem */
padding: 2rem;
margin-bottom: 1.5rem;
/* 边框用 px */
border: 1px solid #ddd;
border-radius: 4px;
/* 字体用 rem */
font-size: 1.6rem;
/* 阴影用 px */
box-shadow: 0 2px 10px rgba(0,0,0,0.1);
}
可访问性考虑
/* 不推荐:固定 px */
html { font-size: 10px; }
/* 推荐:百分比,尊重用户浏览器设置 */
html { font-size: 62.5%; } /* 基于用户默认 16px */
/* 更好的推荐:配合媒体查询 */
html {
font-size: 62.5%;
}
@media (prefers-reduced-motion: reduce) {
html { font-size: 100%; }
}
clamp() 流体排版
/* 字体大小在 16px 到 24px 之间,首选 2vw */
h1 {
font-size: clamp(16px, 2vw, 24px);
}
/* 响应式宽度 */
.container {
width: clamp(320px, 90vw, 1200px);
margin: 0 auto;
}
现代布局最佳实践
/* 1. 根元素基准 */
html {
font-size: 62.5%; /* 1rem = 10px */
}
@media (max-width: 768px) {
html {
font-size: 56.25%; /* 1rem = 9px */
}
}
/* 2. 布局用 rem */
.container {
max-width: 120rem; /* 1200px */
padding: 0 2rem; /* 0 20px */
}
/* 3. 间距用 rem */
.section {
margin-bottom: 8rem; /* 80px */
}
/* 4. 小元素用 px */
.border {
border: 1px solid #ddd;
}
移动端适配方案
rem 方案
function setRem() {
const designWidth = 375; // 设计稿宽度
const baseFontSize = 100; // 1rem = 100px
const width = document.documentElement.clientWidth;
document.documentElement.style.fontSize = (width / designWidth * baseFontSize) + 'px';
}
window.addEventListener('resize', setRem);
/* 设计稿 750px,某元素 200px → 写 2rem */
.element {
width: 2rem; /* 200px */
}
vw 方案
/* 直接基于视口 */
.element {
width: 53.333vw; /* 200/375*100 */
}
/* 配合 CSS 变量 */
:root {
--vw: 1vw;
}
.element {
width: calc(200 * var(--vw));
}
面试要点
- 能够准确说出各单位的计算基准
- 理解 em 和 rem 的区别和使用场景
- 了解 vw/vh 的优缺点,特别是移动端 vh 的问题
- 知道 rem 方案的实现原理
- 了解 clamp() 函数的应用
- 理解为什么推荐 rem + px 混合使用