H5 移动端适配
你有没有遇到过这种情况——设计师给了一份 375px 宽的设计稿,你在自己的手机上开发得美美的,一到测试同学的大屏手机上,按钮变小了、文字挤在一起、布局全乱了?
这就是移动端适配要解决的核心问题:如何让同一套代码,在不同尺寸、不同分辨率的移动设备上,呈现一致(或近似一致)的视觉效果。
本文将从最基础的像素概念讲起,一步步带你理解主流适配方案的原理和实战用法。
移动端适配知识全景图
基础概念:先搞懂"像素"
在深入适配方案之前,必须先理解几个容易混淆但至关重要的概念。很多人写了几年移动端代码都没真正搞清楚这些,导致遇到适配问题时只能"碰运气"。
物理像素(Physical Pixel)
物理像素是屏幕上最小的发光单元,也叫设备像素。它是一个硬件概念,出厂即固定,无法改变。
比如 iPhone 14 的屏幕分辨率是 1170 × 2532,意思是水平方向有 1170 个物理像素点,垂直方向有 2532 个物理像素点。
CSS 像素(CSS Pixel)
CSS 像素是 Web 开发中使用的逻辑像素,也叫设备独立像素(Device Independent Pixel,DIP)。
当你在 CSS 中写 width: 100px 时,这个 100px 就是 CSS 像素。它是一个抽象的度量单位,不一定和屏幕上的物理像素一一对应。
/* 这里的 375px 是 CSS 像素,不是物理像素 */
.container {
width: 375px;
}
设备像素比(DPR)
DPR(Device Pixel Ratio)= 物理像素 / CSS 像素
DPR 描述了物理像素和 CSS 像素之间的换算关系。
用一个简单的例子来理解:
| 设备 | 物理分辨率 | CSS 视口宽度 | DPR |
|---|---|---|---|
| iPhone SE | 750 × 1334 | 375 × 667 | 2 |
| iPhone 14 | 1170 × 2532 | 390 × 844 | 3 |
| iPhone 14 Pro Max | 1290 × 2796 | 430 × 932 | 3 |
| 普通 Android(低端) | 720 × 1280 | 360 × 640 | 2 |
| 普通 Android(高端) | 1080 × 2400 | 360 × 800 | 3 |
你可以在浏览器中通过 window.devicePixelRatio 获取当前设备的 DPR:
console.log(window.devicePixelRatio); // 2 或 3
💡 为什么 DPR 重要? 因为移动端适配的本质是:在不同 DPR、不同 CSS 视口宽度的设备上,让同一份代码呈现一致的布局比例。DPR 影响图片清晰度(需要多倍图),CSS 视口宽度影响布局尺寸(需要适配方案)。
PPI(Pixels Per Inch)
PPI 表示每英寸包含的物理像素数量,也叫像素密度。PPI 越高,屏幕显示越精细。
计算公式:
PPI = √(水平物理像素² + 垂直物理像素²) / 屏幕对角线英寸数
例如 iPhone 14(6.1 英寸屏幕,1170 × 2532):
PPI = √(1170² + 2532²) / 6.1 ≈ 460
PPI 主要是硬件层面的概念,在日常适配开发中直接打交道较少,但它帮助你理解"为什么 iPhone 上 1 个 CSS 像素要用多个物理像素来渲染"——因为屏幕太精细了,如果 1 CSS px = 1 物理 px,东西就会小到看不见。
概念关系图
Viewport 视口详解
理解了像素概念后,接下来要搞清楚视口(Viewport)——这是移动端适配的另一个核心概念。
三种视口
在移动端浏览器中,存在三种不同的视口:
布局视口(Layout Viewport)
浏览器默认的视口宽度。早期移动端浏览器为了能正常显示 PC 端网页,默认将布局视口设为 980px(不同浏览器可能略有差异)。
这意味着,如果你不做任何 viewport 设置,一个 980px 宽的页面会被"完整"地塞进手机屏幕中——结果就是整个页面被极度缩小,用户需要不断捏合放大才能看清内容。
// 获取布局视口宽度
document.documentElement.clientWidth // 默认 980
视觉视口(Visual Viewport)
用户当前实际看到的区域。当用户放大页面时,视觉视口变小(看到的内容更少);缩小页面时,视觉视口变大。
// 获取视觉视口宽度
window.innerWidth
// 或使用 Visual Viewport API(更精确)
window.visualViewport?.width
理想视口(Ideal Viewport)
对于移动端页面来说,我们希望布局视口的宽度等于设备的 CSS 像素宽度(如 iPhone SE 的 375px),这种状态就叫理想视口。此时页面不需要缩放,内容大小刚好适合屏幕。
viewport meta 标签
通过 HTML 中的 <meta name="viewport"> 标签来控制视口行为,这是移动端适配的第一步,也是最关键的一步:
<meta name="viewport" content="width=device-width, initial-scale=1.0">
这行代码的含义是:将布局视口的宽度设置为设备的理想视口宽度,并以 1:1 的比例显示,不缩放。
各属性详解:
| 属性 | 说明 | 常用值 |
|---|---|---|
width | 布局视口的宽度 | device-width 或具体数值 |
height | 布局视口的高度(很少使用) | device-height |
initial-scale | 页面初始缩放比例 | 1.0 |
minimum-scale | 允许的最小缩放比例 | 1.0 |
maximum-scale | 允许的最大缩放比例 | 1.0 或 5.0 |
user-scalable | 是否允许用户手动缩放 | yes / no |
viewport-fit | 视口如何填充屏幕(刘海屏适配) | auto / contain / cover |
不设置 vs 设置 viewport 的对比:
⚠️ 无障碍提醒:
maximum-scale=1.0, user-scalable=no会禁止用户缩放,影响视力障碍用户体验。建议在不影响业务的情况下保留缩放能力:maximum-scale=5.0, user-scalable=yes。
适配方案一:rem 方案
什么是 rem?
rem(root em)是一个 CSS 单位,它的大小相对于根元素 <html> 的 font-size 来计算。
html {
font-size: 16px; /* 浏览器默认值 */
}
.box {
width: 2rem; /* 2 × 16 = 32px */
height: 1.5rem; /* 1.5 × 16 = 24px */
}
核心思路
rem 适配的核心思路非常简单:
- 根据屏幕宽度动态设置
<html>的font-size - 所有元素的尺寸都用
rem单位 - 屏幕变大 → 根字号变大 → 所有元素等比放大;屏幕变小 → 根字号变小 → 所有元素等比缩小
实现方式
假设设计稿宽度为 375px,我们设定基准 font-size 为 100px(选 100 是为了方便换算):
font-size = (屏幕宽度 / 设计稿宽度) × 基准 font-size
= (屏幕宽度 / 375) × 100
方式一:JS 动态计算
// 动态设置根字号
function setRemUnit() {
const docEl = document.documentElement;
const width = docEl.clientWidth;
// 限制最大最小宽度,防止在 PC 或超大屏上比例失调
const clampedWidth = Math.max(320, Math.min(width, 750));
// 以 375px 设计稿为基准,1rem = 100px
docEl.style.fontSize = (clampedWidth / 375) * 100 + 'px';
}
setRemUnit();
// 监听窗口变化
window.addEventListener('resize', setRemUnit);
// 部分浏览器横竖屏切换时不触发 resize
window.addEventListener('orientationchange', setRemUnit);
然后在 CSS 中将设计稿的 px 值除以 100 转为 rem:
/* 设计稿上一个元素: width: 200px, height: 88px, font-size: 28px */
.card {
width: 2rem; /* 200 / 100 = 2rem */
height: 0.88rem; /* 88 / 100 = 0.88rem */
font-size: 0.28rem; /* 28 / 100 = 0.28rem */
}
💡 基准选
100px而不是其他值,纯粹是为了心算方便:设计稿上的28px直接除以 100 变成0.28rem,不需要计算器。
方式二:flexible.js(阿里方案)
lib-flexible 是阿里巴巴开源的移动端适配方案,它的核心逻辑和上面的手动实现类似:
// lib-flexible 的简化版核心逻辑
(function flexible(window, document) {
const docEl = document.documentElement;
// 获取 DPR
const dpr = window.devicePixelRatio || 1;
// 设置 body 的字号
function setBodyFontSize() {
if (document.body) {
document.body.style.fontSize = 12 * dpr + 'px';
} else {
document.addEventListener('DOMContentLoaded', setBodyFontSize);
}
}
setBodyFontSize();
// 将屏幕宽度分为 10 份,1rem = 屏幕宽度 / 10
function setRemUnit() {
const rem = docEl.clientWidth / 10;
docEl.style.fontSize = rem + 'px';
}
setRemUnit();
window.addEventListener('resize', setRemUnit);
window.addEventListener('pageshow', function (e) {
if (e.persisted) {
setRemUnit();
}
});
})(window, document);
⚠️
lib-flexible的作者已经在 GitHub 上声明:由于 viewport 单位(vw)的兼容性已经足够好,建议使用 vw 方案替代 lib-flexible。不过在旧项目维护中你可能仍然会遇到它。
rem 方案的换算公式
不同的实现方式对应不同的换算方式:
| 实现方式 | 基准 font-size | 换算公式 | 设计稿 28px → |
|---|---|---|---|
| 手动实现(基准 100) | 屏幕宽度/375 × 100 | px / 100 | 0.28rem |
| flexible.js | 屏幕宽度 / 10 | px / (375/10) = px / 37.5 | 0.7467rem |
rem 方案的优缺点
优点:
- 兼容性好,支持 iOS 6+、Android 2.1+
- 等比缩放效果好,各设备表现一致
- 生态成熟,有丰富的 PostCSS 插件支持
缺点:
- 依赖 JS:必须在页面加载时执行 JS 计算根字号,若 JS 加载延迟可能闪烁
- 非整数精度问题:除法运算结果是小数,存在舍入误差
- 不够"原生":本质上是一种 hack,利用了
font-size来做布局换算
适配方案二:vw/vh 方案(推荐)
什么是 vw/vh?
vw 和 vh 是 CSS3 引入的视口单位(Viewport Units):
1vw= 视口宽度的 1%1vh= 视口高度的 1%1vmin=vw和vh中较小的那个1vmax=vw和vh中较大的那个
核心思路
vw 方案的核心思路更加直接:将设计稿上的 px 值按比例直接转换为 vw 单位。
换算公式:
vw 值 = (设计稿元素尺寸px / 设计稿宽度px) × 100
以 375px 设计稿为例:
1px = (1 / 375) × 100 = 0.2667vw
使用示例
/* 设计稿(375px 宽)上的元素: width: 200px, font-size: 16px, padding: 20px */
.card {
width: 53.33vw; /* 200 / 375 × 100 = 53.33vw */
font-size: 4.267vw; /* 16 / 375 × 100 = 4.267vw */
padding: 5.333vw; /* 20 / 375 × 100 = 5.333vw */
}
显然,手动计算这些值太麻烦了。实际项目中都是通过 PostCSS 插件自动转换。
vw 方案的优缺点
优点:
- 不依赖 JS:纯 CSS 实现,无需运行时计算
- 精度高:浏览器原生支持视口单位,计算精度优于 rem
- 语义清晰:
vw本身就表示"视口宽度百分比",概念直观 - 性能好:无需监听
resize事件和动态修改 DOM
缺点:
- 兼容性门槛:需要 iOS 8+、Android 4.4+(现在几乎不是问题了)
- 无法限制最大/最小宽度:在超大或超小屏幕上可能需要额外处理
- 字号跟随视口完全等比:在 iPad 等大屏设备上,文字可能过大
适配方案三:rem + vw 混合方案
为了结合两种方案的优势,业界提出了一种混合方案:用 vw 来设置根元素 font-size,布局尺寸仍用 rem。
/* 以 375px 设计稿为例,基准 font-size = 100px */
/* 100px / 375px × 100 = 26.667vw */
html {
font-size: 26.667vw;
}
/* 限制最大最小宽度 */
@media screen and (max-width: 320px) {
html {
font-size: 85.33px; /* 320 / 375 × 100 */
}
}
@media screen and (min-width: 750px) {
html {
font-size: 200px; /* 750 / 375 × 100 */
}
}
/* 布局使用 rem */
.card {
width: 2rem; /* 200px */
font-size: 0.28rem; /* 28px */
}
混合方案的优势:
适配方案四:百分比方案
百分比方案是最"朴素"的适配思路——所有尺寸用百分比表示,让元素相对于父元素自适应。
.container {
width: 100%;
padding: 0 4.267%; /* 16px / 375px */
}
.card {
width: 48%;
margin-bottom: 4%;
}
百分比方案的问题:
不同 CSS 属性的百分比参照物不一样,这让开发变得混乱:
| CSS 属性 | 百分比参照 |
|---|---|
width | 父元素的 width |
height | 父元素的 height |
padding | 父元素的 width(注意不是 height) |
margin | 父元素的 width |
font-size | 父元素的 font-size |
border-radius | 元素自身的宽/高 |
top / left | 定位父元素的高/宽 |
由于参照物不统一,百分比方案在复杂布局中维护困难,通常只在简单的自适应布局中使用,不推荐作为主适配方案。
适配方案五:响应式布局
响应式布局(Responsive Layout)不是"等比缩放",而是通过媒体查询在不同屏幕尺寸下使用不同的布局方式。
/* 手机端:单列布局 */
.grid {
display: flex;
flex-wrap: wrap;
}
.grid-item {
width: 100%;
}
/* 平板端:两列布局 */
@media screen and (min-width: 768px) {
.grid-item {
width: 50%;
}
}
/* PC端:四列布局 */
@media screen and (min-width: 1024px) {
.grid-item {
width: 25%;
}
}
响应式 vs 自适应(等比缩放):
| 特点 | 响应式布局 | 自适应(rem/vw 等比缩放) |
|---|---|---|
| 核心思路 | 不同屏幕用不同布局 | 同一布局等比缩放 |
| 适用场景 | 需要同时适配手机+平板+PC | 只需适配不同手机 |
| 开发成本 | 高(需要写多套样式) | 低(一套样式) |
| 用户体验 | 更好(针对性优化) | 一般(大屏只是放大版) |
| 典型场景 | 官网、新闻站、博客 | H5 活动页、APP 内嵌页 |
💡 实际项目中:纯 H5 移动端页面(如电商活动页、APP 内嵌页)通常使用 vw 或 rem 等比缩放方案;需要适配多端的项目(如官网)通常使用响应式布局。
PostCSS 插件实战
手动换算 px 到 rem 或 vw 既繁琐又容易出错。在实际项目中,都是通过 PostCSS 插件自动完成转换——你正常写 px,构建时插件自动帮你换算。
postcss-pxtorem(rem 方案)
将 px 自动转换为 rem。
安装:
npm install postcss-pxtorem --save-dev
配置(postcss.config.js):
module.exports = {
plugins: {
'postcss-pxtorem': {
// 根元素字体大小(对应 375 设计稿下 1rem = 100px)
rootValue: 100,
// 允许转换的 CSS 属性,* 代表所有
propList: ['*'],
// 忽略的选择器,.norem- 开头的类不转换
selectorBlackList: ['.norem-'],
// 最小转换像素值,小于 2px 不转换(避免边框被转换)
minPixelValue: 2,
// 不转换媒体查询中的 px
mediaQuery: false,
},
},
};
效果:
/* 输入(你写的代码) */
.card {
width: 200px;
height: 88px;
font-size: 28px;
border: 1px solid #eee; /* 1px < 2px,不转换 */
}
/* 输出(构建后的代码) */
.card {
width: 2rem;
height: 0.88rem;
font-size: 0.28rem;
border: 1px solid #eee; /* 保持 1px */
}
postcss-px-to-viewport(vw 方案)
将 px 自动转换为 vw。
安装:
npm install postcss-px-to-viewport --save-dev
配置(postcss.config.js):
module.exports = {
plugins: {
'postcss-px-to-viewport': {
// 设计稿宽度
viewportWidth: 375,
// 转换后保留的小数位数
unitPrecision: 5,
// 需要转换的 CSS 属性,* 代表全部
propList: ['*'],
// 转换成的视口单位
viewportUnit: 'vw',
// font 相关属性的视口单位
fontViewportUnit: 'vw',
// 最小转换值,小于 1px 不转换
minPixelValue: 1,
// 是否转换媒体查询中的 px
mediaQuery: false,
// 忽略的选择器
selectorBlackList: ['.novw-'],
// 忽略的文件
exclude: [/node_modules/],
},
},
};
效果:
/* 输入 */
.card {
width: 200px;
font-size: 16px;
padding: 20px;
border: 1px solid #eee;
}
/* 输出 */
.card {
width: 53.33333vw;
font-size: 4.26667vw;
padding: 5.33333vw;
border: 1px solid #eee;
}
💡 postcss-px-to-viewport 已停止维护,推荐使用升级版 postcss-px-to-viewport-8-plugin,API 完全兼容。
不需要转换时怎么办?
有时候某些属性不希望被插件转换(比如 1px 边框或特殊的固定尺寸),可以这样处理:
/* 方法一:使用大写 PX 或 Px(postcss-pxtorem 默认不转换大写) */
.border {
border: 1PX solid #eee;
}
/* 方法二:使用 selectorBlackList 中的选择器前缀 */
.norem-title {
font-size: 32px; /* 不会被转换 */
}
/* 方法三:使用行内注释跳过 */
.special {
width: 200px; /* 会被转换 */
height: 100px; /* postcss-pxtorem: off */ /* 不会被转换 */
}
方案选型指南
面对这么多适配方案,实际项目中到底该怎么选?下面是一个简洁的决策流程:
主流方案对比总表
| 特性 | rem 方案 | vw 方案 | rem + vw 混合 | 响应式布局 |
|---|---|---|---|---|
| 依赖 JS | 是 | 否 | 否 | 否 |
| 换算精度 | 有小数误差 | 浏览器原生计算 | 浏览器原生计算 | 不涉及 |
| 兼容性 | iOS 6+ | iOS 8+ | iOS 8+ | 所有 |
| 宽度极值控制 | JS 中 clamp | 需额外处理 | 媒体查询控制 | 天然支持 |
| 开发体验 | PostCSS 自动 | PostCSS 自动 | PostCSS 自动 | 需写多套 |
| 适用场景 | 旧项目 | 新项目首选 | 需极值控制 | 多端适配 |
| 当前趋势 | 逐渐被替代 | 主流方案 | 精细控制 | 多端必选 |
💡 2024+ 新项目推荐:直接使用 vw 方案,搭配
postcss-px-to-viewport-8-plugin。如果需要限制大屏最大宽度,使用rem + vw混合方案或 CSSclamp()函数。
进阶技巧
使用 CSS clamp() 限制字号范围
clamp() 函数可以设置一个值的最小值、首选值和最大值,完美解决 vw 方案在极端屏幕上字号失控的问题:
/* clamp(最小值, 首选值, 最大值) */
.title {
/* 字号最小 14px,最大 20px,中间按视口比例变化 */
font-size: clamp(14px, 4.267vw, 20px);
}
.container {
/* 宽度最小 320px,最大 750px */
max-width: clamp(320px, 100vw, 750px);
margin: 0 auto;
}
安全区域适配
现代手机的刘海屏、底部横条(Home Indicator)可能遮挡页面内容。使用 env() 函数配合 viewport-fit=cover 进行适配:
<meta name="viewport" content="width=device-width, initial-scale=1.0, viewport-fit=cover">
/* 底部固定栏需要加上安全区域间距 */
.bottom-bar {
position: fixed;
bottom: 0;
left: 0;
width: 100%;
/* 兼容写法 */
padding-bottom: constant(safe-area-inset-bottom); /* iOS < 11.2 */
padding-bottom: env(safe-area-inset-bottom); /* iOS >= 11.2 */
}
/* 也可以用 calc 叠加 */
.bottom-bar {
padding-bottom: calc(12px + env(safe-area-inset-bottom));
}
横屏适配
当用户旋转手机进入横屏时,视口宽度突然变大,基于 vw 的布局可能变形。常见处理方式:
/* 方案一:提示用户竖屏使用 */
@media screen and (orientation: landscape) {
.landscape-tip {
display: flex; /* 展示"请旋转手机"提示 */
}
}
/* 方案二:横屏时使用 vh 代替 vw */
@media screen and (orientation: landscape) {
html {
font-size: calc(100vh / 667 * 100); /* 667 是设计稿高度 */
}
}
1px 边框问题
高清屏(DPR >= 2)上 CSS 的 1px 实际显示为 2 个或 3 个物理像素,看起来偏粗。最推荐的解决方案:
/* 伪元素 + transform 缩放 */
.thin-border {
position: relative;
}
.thin-border::after {
content: '';
position: absolute;
left: 0;
top: 0;
width: 200%;
height: 200%;
border: 1px solid #ddd;
border-radius: 8px; /* 圆角也要 2 倍 */
transform: scale(0.5);
transform-origin: left top;
pointer-events: none;
box-sizing: border-box;
}
完整项目配置示例
以一个基于 Vite + Vue 3 的移动端项目为例,展示完整的 vw 适配配置:
项目结构
my-h5-app/
├── src/
│ ├── main.ts
│ ├── App.vue
│ └── views/
│ └── Home.vue
├── index.html
├── postcss.config.js
├── vite.config.ts
└── package.json
1. index.html — 设置 viewport
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport"
content="width=device-width, initial-scale=1.0, maximum-scale=5.0, viewport-fit=cover">
<title>H5 App</title>
</head>
<body>
<div id="app"></div>
<script type="module" src="/src/main.ts"></script>
</body>
</html>
2. postcss.config.js — 自动 px 转 vw
module.exports = {
plugins: {
'postcss-px-to-viewport-8-plugin': {
viewportWidth: 375,
unitPrecision: 5,
propList: ['*'],
viewportUnit: 'vw',
fontViewportUnit: 'vw',
minPixelValue: 1,
mediaQuery: false,
selectorBlackList: ['.ignore-'],
exclude: [/node_modules/],
},
},
};
3. 组件中正常写 px
<template>
<div class="home">
<h1 class="title">欢迎来到首页</h1>
<div class="card">
<p class="desc">按照 375px 设计稿正常写 px 即可</p>
</div>
</div>
</template>
<style scoped>
.home {
padding: 20px 16px;
}
.title {
font-size: 32px;
font-weight: bold;
margin-bottom: 16px;
color: #333;
}
.card {
background: #f5f5f5;
border-radius: 12px;
padding: 24px 20px;
}
.desc {
font-size: 14px;
color: #666;
line-height: 1.6;
}
/* 不需要转换的元素加上 ignore- 前缀 */
.ignore-border {
border: 1px solid #eee;
}
</style>
构建后,所有 px 会自动转为 vw:
.home {
padding: 5.33333vw 4.26667vw;
}
.title {
font-size: 8.53333vw;
margin-bottom: 4.26667vw;
}
/* ... */
面试高频问答
Q1:移动端适配的核心原理是什么?
答:移动端适配的核心原理是让页面元素的尺寸能够随设备屏幕宽度等比缩放。由于不同设备的 CSS 视口宽度不同(如 iPhone SE 是 375px,iPhone 14 是 390px),直接写死 px 值会导致布局在不同设备上不一致。通过 rem(相对根字号)或 vw(相对视口宽度百分比)等相对单位,建立元素尺寸与屏幕宽度之间的比例关系,实现"一套代码,多设备适配"。
Q2:物理像素、CSS 像素、DPR 之间的关系?
答:
- 物理像素是屏幕上最小的发光点,由硬件决定,不可改变;
- CSS 像素是 Web 开发中使用的逻辑像素,是一个抽象单位;
- DPR = 物理像素 / CSS 像素,描述两者的换算关系。
例如 iPhone SE 的 DPR 为 2,意味着 1 个 CSS 像素对应 2×2 = 4 个物理像素。DPR 的存在是因为高清屏幕像素密度很高,如果 1 CSS px = 1 物理 px,页面元素会小到无法操作。
Q3:rem 适配方案的实现原理?
答:rem 方案分两步:
- 动态设置根字号:通过 JS 监听屏幕宽度变化,按比例计算
<html>的font-size。例如以 375px 设计稿为基准,设font-size = 屏幕宽度 / 375 × 100; - 使用 rem 单位:所有元素尺寸用
rem表示(设计稿 px 值 / 基准值),当屏幕变大时根字号变大,所有 rem 值等比放大。
实际开发中使用 postcss-pxtorem 插件自动完成 px 到 rem 的转换。
Q4:vw 方案和 rem 方案有什么区别?哪个更好?
答:
| 对比项 | rem 方案 | vw 方案 |
|---|---|---|
| 依赖 JS | 是 | 否 |
| 精度 | 有小数舍入误差 | 浏览器原生计算,精度高 |
| 兼容性 | iOS 6+ | iOS 8+(已全面支持) |
| 性能 | 需监听 resize | 纯 CSS,性能更好 |
vw 方案更好。它不依赖 JS、精度更高、性能更好,且兼容性在当下已不是问题。rem 方案的主流地位正被 vw 取代,新项目推荐直接使用 vw 方案。
Q5:postcss-px-to-viewport 的作用是什么?怎么配置?
答:postcss-px-to-viewport 是一个 PostCSS 插件,在构建阶段自动将 CSS 中的 px 单位转换为 vw 单位。开发者只需按设计稿写 px,无需手动计算。
关键配置项:
viewportWidth: 375:设计稿宽度propList: ['*']:转换所有属性minPixelValue: 1:小于 1px 的值不转换selectorBlackList:不需要转换的选择器
当前推荐使用升级版 postcss-px-to-viewport-8-plugin。
Q6:移动端 viewport 标签每个属性是什么意思?
答:<meta name="viewport"> 控制移动端视口行为:
width=device-width:将布局视口宽度设为设备 CSS 像素宽度(理想视口)initial-scale=1.0:初始缩放为 1,页面不缩放maximum-scale/minimum-scale:最大/最小缩放比例user-scalable:是否允许用户手动缩放viewport-fit=cover:让页面延伸到安全区域外(适配刘海屏)
不设置 viewport 时,布局视口默认 980px,页面会被缩小显示在手机上。
Q7:什么是布局视口、视觉视口和理想视口?
答:
- 布局视口(Layout Viewport):浏览器默认的页面布局宽度,默认 980px,页面按此宽度渲染后缩小到屏幕内;
- 视觉视口(Visual Viewport):用户当前实际看到的区域,缩放会改变其大小;
- 理想视口(Ideal Viewport):布局视口宽度等于设备 CSS 像素宽度(如 375px),通过
width=device-width实现。
移动端适配的第一步就是设置 viewport meta 标签,将布局视口设为理想视口。
Q8:如何解决 vw 方案在大屏上字号过大的问题?
答:两种方案:
- CSS
clamp()函数:font-size: clamp(14px, 4.267vw, 20px),设置字号的最小值和最大值; - rem + vw 混合方案:用 vw 设置根字号,配合媒体查询限制极值,布局单位用 rem。
Q9:flexible.js 是什么?现在还需要使用吗?
答:flexible.js(lib-flexible)是阿里巴巴开源的 rem 适配方案,核心是将屏幕宽度十等分来设置根字号,并根据 DPR 设置 viewport 缩放。
现在不推荐使用了。其作者已明确声明由于 vw 单位兼容性足够好,建议用 vw 方案替代。flexible.js 主要在维护老项目时可能遇到。
Q10:在实际项目中如何选择适配方案?
答:
- 纯移动端 H5(活动页、APP 内嵌页):使用 vw 方案 +
postcss-px-to-viewport-8-plugin - 需要限制最大宽度(如 iPad 浏览):使用 rem + vw 混合方案或 CSS
clamp() - 维护旧项目(已使用 flexible.js):继续用 rem 方案,不需要迁移
- 多端适配(手机 + 平板 + PC):使用响应式布局 + 媒体查询
- 简单自适应(元素宽度跟随容器):百分比 + Flex/Grid 即可