告别布局困扰:手把手教你玩转 CSS 相对单位 vw/vh、em、rem

308 阅读6分钟

在 CSS 的世界里,精确控制元素尺寸是构建现代、响应式网页的基石。除了我们熟知的像素 (px),相对长度单位提供了更具灵活性和可维护性的方案。本文将深入探讨三种最常用的相对单位:vh / vwemrem,帮助你彻底理解它们并应用于实际项目。

1 引言

绝对单位(如 px)是固定的,一个 100px 宽的元素在任何设备上都是 100px。而相对单位则不同,它们的值是相对于另一个参考值(如视口尺寸、父元素或根元素的字体大小)来计算的。这种特性使得它们天生适配各种屏幕尺寸和设备,成为响应式设计的首选。

2 vh 和 vw:视口比例单位

2.1 概念

vhvw 是相对于浏览器视口(Viewport) 尺寸的单位。

  • 1vh = 视口高度的 1%
  • 1vw = 视口宽度的 1%

例如,一个 50vw 宽的元素,其宽度将始终是视口宽度的一半。

2.2 特点

  • 直接响应视口:尺寸随用户调整浏览器窗口大小而立即变化,响应非常直接。
  • 百分比替代:在某些场景下比使用百分比 (%) 更简单,因为它不需要依赖父元素的尺寸。
  • 计算简单100vh 就是整个屏幕的高度,100vw 就是整个屏幕的宽度。

2.3 代码示例

<div class="hero-banner">
  <h1>欢迎来到我的网站</h1>
</div>

<div class="sidebar">侧边栏</div>
/* 创建一个占满整个屏幕高度的区域 */
.hero-banner {
  height: 100vh; /* 高度为视口的100% */
  width: 100vw;   /* 宽度为视口的100% */
  background-color: lightblue;
  display: flex;
  justify-content: center;
  align-items: center;
  flex-direction: column;
}

/* 创建一个始终占视口宽度20%的侧边栏 */
.sidebar {
  width: 20vw;
  height: 100%;
  background-color: #f1f1f1;
  position: fixed;
  top: 0;
  left: 0;
}

2.4 场景

  1. **全屏区块:快速实现一个占满整个屏幕的区域。
  2. 弹窗或蒙层:创建覆盖整个视口的模态框或背景遮罩。
  3. 保持纵横比的元素:结合 padding 的巧妙用法(如 padding-top: 56.25% 用于 16:9 视频)来实现。
  4. 字体大小:使用 vw 设置标题字体,使其随视口宽度缩放,创造出动态的排版效果(需谨慎使用,可结合 clamp() 函数避免过大或过小)。

3 em:基于上下文的相对单位

3.1 概念

em 是一个相对于当前元素的字体大小(font-size)的单位。

  • 如果当前元素没有设置字体大小,则它会继承父元素的字体大小,并以此作为计算基准。
  • 1em = 当前元素的 font-size 值。
  • 当用于设置 width, height, margin, padding 等非字体属性时,其计算值同样基于当前元素的 font-size

核心要点em 的参考基准是“当前元素的字体大小”,而不是父元素的字体大小。只是当自身未设置时,这个“当前字体大小”恰好等于父级的。

3.2 特点

  • 级联效应:在嵌套结构中,如果每一层都使用 em 定义字体大小,那么每一层的 1em 值都可能不同,变化会层层叠加,可能导致意想不到的放大或缩小。
  • 高度上下文相关:非常适合用于控制与文本排版紧密相关的组件内部间距和大小。

3.3 代码示例

<article class="blog-post">
  <h2>这是一个标题</h2>
  <p>这是一段段落内容,里面有一个 <span>突出显示</span> 的文字。</p>
  <button>阅读更多</button>
</article>
.blog-post {
  font-size: 18px; /* 为组件设置基础上下文 */
  padding: 1.5em; /* padding = 1.5 * 18px = 27px */
}

.blog-post h2 {
  font-size: 1.5em; /* 字体大小 = 1.5 * 18px = 27px */
  margin-bottom: 0.5em; /* margin-bottom = 0.5 * 27px = 13.5px */
}

.blog-post p {
  font-size: 1em; /* 字体大小 = 1 * 18px = 18px */
  line-height: 1.6em; /* line-height = 1.6 * 18px = 28.8px */
}

.blog-post span {
  font-size: 0.9em; /* 字体大小 = 0.9 * 18px = 16.2px */
}

.blog-post button {
  padding: 0.75em 1.5em; /* 上下:0.75*18px=13.5px, 左右:1.5*18px=27px */
  font-size: 0.9em;
}

3.4 场景

  1. 组件化开发:在单个组件(如卡片、弹窗)内部,使用 em 来设置内边距、外边距、元素大小等,确保组件内所有尺寸与组件的基准字体大小成比例。更改组件的字体大小,其内部布局会自动按比例调整。
  2. 按钮和表单元素:用 em 设置 padding,可以让按钮的内边距随其字体大小自适应变化。

4 rem:基于根元素的相对单位

4.1 概念

rem (Root EM) 是相对于根元素(<html> 的字体大小(font-size)的单位。

  • 1rem = 根元素 (html) 的 font-size 值。
  • 无论元素嵌套多深,1rem 的值在整个页面中都是固定的(除非根元素的字体大小改变)。

4.2 特点

  • 一致性:避免了 em 的级联问题,所有基于 rem 的尺寸都参考同一个值,易于预测和维护。
  • 易于控制和缩放:只需改变根元素的 font-size,整个页面的布局和元素大小都会按比例缩放,这是实现移动端适配的核心原理。

4.3 代码示例

一种常见的移动端适配技巧是通过媒体查询动态改变 htmlfont-size

<header>
  <h1>网站标题</h1>
  <nav>...</nav>
</header>
<main>
  <section class="card">这是一张卡片</section>
</main>
/* 设置根元素基准值(常用设计稿宽度为750px,此时1rem=100px,方便计算) */
html {
  font-size: 100px; 
}
/* 实际书写时,将设计稿上的尺寸除以100,得到rem值 */
body {
  font-size: 0.16rem; /* 相当于 16px */
}

header {
  height: 1rem; /* 相当于 100px */
  padding: 0.2rem; /* 相当于 20px */
}

h1 {
  font-size: 0.36rem; /* 相当于 36px */
}

.card {
  width: 3.45rem; /* 相当于 345px */
  margin: 0.1rem auto;
  padding: 0.2rem;
  border-radius: 0.1rem;
}

/* 媒体查询,在不同屏幕宽度下调整根字体大小,实现适配 */
@media screen and (max-width: 480px) {
  html {
    font-size: 50px; /* 小屏幕上,基准值变小,所有rem尺寸等比例缩小 */
  }
}
/* 更优雅的做法是使用CSS变量和JS动态计算根字体大小 */

4.4 场景

  1. 全局布局和间距:用于设置容器宽度、内外边距、定位偏移等,保证整个布局的协调和可缩放性。
  2. 移动端适配:这是 rem 最核心的应用。通过 JS 或媒体查询根据屏幕宽度动态计算并设置 htmlfont-size,页面所有使用 rem 的元素都会自动等比缩放,完美适配不同尺寸的手机屏幕。
  3. 可访问性:用户如果调整了浏览器的默认字体大小,使用 rem 的元素会尊重用户的设置而缩放,而使用 px 的则不会。

5 总结与对比

特性vh / vwemrem
参考基准视口(Viewport)的尺寸当前元素font-size根元素(<html>font-size
级联性有(受自身和父级字体影响)无(全局统一)
主要应用场景全屏布局、视口比例元素组件内部、与文字相关的间距全局布局、移动端适配、可访问性
** predictability**中等(在复杂嵌套中需小心)