4. vw/vh适配方案:从原理到实战

说到移动端适配,vw/vh 这套方案我用了好几年了。说实话,刚接触时我也觉得它跟 rem 差不多,后来踩了几个坑才真正理解它的价值。今天咱们就把它彻底聊透。

4.1 vw/vh 单位原理

vw 和 vh 是啥?简单说:

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

举个例子。iPhone 6 的宽度是 375px。那么:

  • 100vw = 375px
  • 1vw = 3.75px
  • 50vw = 187.5px

嗯,就是这么直白。它不像 rem 那样需要计算根字体大小,vw 直接跟视口挂钩。

核心区别:vw 是相对视口的,rem 是相对根字体的。一个是「看屏幕脸色」,一个是「看 html 字号脸色」。

我个人习惯把 vw 用在宽度相关的属性上,vh 用在高度上。但要注意,vh 在移动端有个坑——地址栏会变化,导致 100vh 比实际可视区域大。这个后面细说。

4.2 vw vs rem:到底选哪个?

我在项目中两种方案都用过。来,直接上对比:

对比维度 vw/vh rem
计算方式 直接相对视口 通过根字体间接计算
精度 高,无中间转换误差 有小数点精度问题
兼容性 iOS 8+ / Android 4.4+ iOS 6+ / Android 2.1+
字体适配 直接可用 需要额外计算
高度适配 支持 vh 不支持,需配合其他方案

你想想看,rem 其实多了一层「代理」。你要先算好设计稿宽度对应的根字号,然后所有尺寸都基于这个字号。vw 就省事多了——设计稿 750px,那 100vw 就是 750px,直接换算比例就行。

我的建议:新项目直接用 vw。老项目如果兼容性要求高(比如要支持 Android 4.3 以下),才考虑 rem。

4.3 纯 CSS 适配方案

好,咱们直接上代码。这是我最常用的一套方案:

/* 基础设置 */
html {
  /* 设计稿宽度 750px,1vw = 7.5px */
  font-size: calc(100vw / 7.5);
}

/* 容器适配 */
.container {
  width: 100vw;
  max-width: 750px;
  margin: 0 auto;
}

/* 元素尺寸 */
.box {
  /* 设计稿 300px → 300/7.5 = 40vw */
  width: 40vw;
  height: 20vw;
  /* 设计稿 20px → 20/7.5 = 2.6667vw */
  font-size: 2.6667vw;
  padding: 1.3333vw;
}

等等,每次都要手动算?当然不是。我一般用 SCSS 写个函数:

// 设计稿宽度
$design-width: 750;

// px 转 vw
@function vw($px) {
  @return ($px / $design-width * 100) * 1vw;
}

// 使用
.box {
  width: vw(300);   // → 40vw
  height: vw(150);  // → 20vw
  font-size: vw(20); // → 2.6667vw
}

说实话,这套方案我用了两年多,基本没出过问题。但要注意一点——字体用 vw 时,在超大屏上会变得特别大。我一般会加个 max 限制:

body {
  font-size: 2.6667vw;
  /* 最大不超过 20px */
  max-font-size: 20px;
}

/* 或者用 clamp */
body {
  font-size: clamp(12px, 2.6667vw, 20px);
}

4.4 兼容性处理

嗯,这里要重点说。vw/vh 的兼容性其实比很多人想象的好:

  • iOS:8.0 以上完全支持
  • Android:4.4 以上完全支持
  • 微信内置浏览器:完全支持

但我在项目中遇到过几个坑:

坑一:100vh 在 iOS Safari 上的问题

iOS Safari 的地址栏会占用空间,100vh 实际比可视区域大。我曾经有个全屏页面,底部总被地址栏挡住。

解决方案:用 100dvh(动态视口高度)代替 100vh,或者用 JS 动态计算。

/* 推荐方案 */
.full-screen {
  height: 100vh;
  /* 降级方案 */
  height: -webkit-fill-available;
  height: 100dvh;
}

坑二:Android 低版本不支持 vw 单位

Android 4.3 及以下不支持 vw。虽然现在很少见了,但如果你要兼容,可以用 polyfill。

我一般这样处理兼容性:

/* 先给个 fallback */
.box {
  width: 300px;
  width: 40vw;
}

/* 或者用 @supports 检测 */
@supports (width: 1vw) {
  .box {
    width: 40vw;
  }
}

避坑指南:我曾经在某个项目中,vw 和 rem 混用,结果字体忽大忽小。后来统一用 vw,问题就解决了。记住——要么全用 vw,要么全用 rem,别混着来。

4.5 实战建议

最后,分享几个我个人的经验:

  1. 设计稿 750px 最顺手——1vw = 7.5px,好算
  2. 字体用 vw 要加 clamp——防止超大屏上字体爆炸
  3. 高度适配用 vh 要小心——100vh 不等于可视高度
  4. 配合 flex 布局更香——vw 控制容器,flex 控制内部排列

说白了,vw/vh 方案就是「让浏览器帮你算」。你只要告诉它「这个元素占屏幕的 40%」,剩下的交给 CSS。比起 rem 那套「先算根字号再算元素尺寸」的弯弯绕,vw 明显更直接。

好了,这一章就到这里。下一章咱们聊聊「视口单位在实战中的更多坑」,包括横竖屏切换、刘海屏适配这些头疼的问题。