做H5项目,第一件事就是确定适配方案。vw、rem、百分比、flexible.js……方案太多了,新手很容易选错。
今天把主流方案理一遍,给出明确的选择建议。
方案一:rem + 动态font-size
这是最经典的方案,早期移动端几乎都用它。
// 以750设计稿为例
function setRem() {
const baseSize = 75; // 750 / 10
const scale = document.documentElement.clientWidth / 750;
document.documentElement.style.fontSize = baseSize * Math.min(scale, 2) + 'px';
}
window.addEventListener('resize', setRem);
setRem();
/* 设计稿上100px的元素 */
.box {
width: calc(100 / 75 * 1rem); /* 或直接用postcss转换 */
}
缺点:需要JS介入,字体大小不好控制,容易出现"字体缩放后模糊"的问题。
方案二:vw
vw是相对视口宽度的单位,1vw = 视口宽度的1%。纯CSS方案,不需要JS。
/* 750设计稿,1px = 100/750 vw */
.box {
width: 13.3333vw; /* 设计稿100px */
height: 6.6667vw; /* 设计稿50px */
}
实际项目里用PostCSS自动换算:
npm install postcss-px-to-viewport -D
// postcss.config.js
module.exports = {
plugins: {
'postcss-px-to-viewport': {
viewportWidth: 375, // 设计稿宽度
unitPrecision: 5,
viewportUnit: 'vw',
selectorBlackList: ['.ignore-'], // 不转换的类名
minPixelValue: 1,
mediaQuery: false
}
}
};
写代码时还是写px,构建时自动转成vw,体验很好。
方案三:flexible + rem(阿里方案)
amfe-flexible是阿里出的方案,本质还是rem,但处理了dpr和1px边框问题:
import 'amfe-flexible';
// postcss-pxtorem
{
rootValue: 37.5,
propList: ['*'],
selectorBlackList: ['.norem']
}
这个方案的优点是生态成熟,缺点是依赖JS,而且新版amfe-flexible已经不怎么维护了。
方案四:媒体查询 + 百分比
.container {
width: 100%;
max-width: 750px;
margin: 0 auto;
}
.box {
width: 50%;
}
@media (min-width: 768px) {
.box { width: 33.33%; }
}
这个方案适合内容型页面(文章、列表),不适合精细的视觉还原。
对比总结
| 方案 | 依赖JS | 精度 | 维护性 | 适合场景 |
|---|---|---|---|---|
| rem + JS | 是 | 高 | 一般 | 老项目、精细还原 |
| vw | 否 | 高 | 好 | 新项目首选 |
| flexible | 是 | 高 | 一般 | 需要处理1px问题 |
| 百分比 | 否 | 低 | 好 | 内容型页面 |
我的建议
新项目直接用vw。 理由:
- 纯CSS,不需要JS,首屏不会闪
- PostCSS自动转换,开发体验和写px一样
- 兼容性已经很好(iOS 9+、Android 4.4+)
- 不需要处理font-size的边界问题
// 完整的postcss配置
module.exports = {
plugins: {
'postcss-px-to-viewport-8-plugin': {
unitToConvert: 'px',
viewportWidth: (file) => {
// 区分PC和移动端
if (file.indexOf('pc') !== -1) return 1920;
return 375;
},
unitPrecision: 5,
viewportUnit: 'vw',
fontViewportUnit: 'vw',
selectorBlackList: ['.pc-'],
minPixelValue: 1,
mediaQuery: false,
replace: true,
exclude: [/node_modules/]
}
}
};
需要注意的几个坑
1. 1px边框问题
在2倍屏上,1px会被渲染成2个物理像素。解决方案:
.border {
border: 1px solid #ddd;
}
@media (-webkit-min-device-pixel-ratio: 2) {
.border {
border-width: 0.5px;
}
}
2. 字体大小不要用vw
标题字号用vw在超宽屏上会大得离谱,可以用clamp:
.title {
font-size: clamp(18px, 4vw, 28px);
}
3. 大屏限制
移动端页面在PC上打开会拉伸变形,加个最大宽度:
#app {
max-width: 750px;
margin: 0 auto;
}
适配方案没有银弹,vw在绝大多数场景下够用。真遇到特殊需求,混用方案也不丢人。