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 实战建议
最后,分享几个我个人的经验:
- 设计稿 750px 最顺手——1vw = 7.5px,好算
- 字体用 vw 要加 clamp——防止超大屏上字体爆炸
- 高度适配用 vh 要小心——100vh 不等于可视高度
- 配合 flex 布局更香——vw 控制容器,flex 控制内部排列
说白了,vw/vh 方案就是「让浏览器帮你算」。你只要告诉它「这个元素占屏幕的 40%」,剩下的交给 CSS。比起 rem 那套「先算根字号再算元素尺寸」的弯弯绕,vw 明显更直接。
好了,这一章就到这里。下一章咱们聊聊「视口单位在实战中的更多坑」,包括横竖屏切换、刘海屏适配这些头疼的问题。