移动端适配主要是为了解决不同设备屏幕尺寸和分辨率带来的显示问题,如何让一个网页在不同设备上都能有良好的显示效果,是移动端开发的核心问题之一。
物理像素
物理像素是指屏幕上最小的物理显示单元,是屏幕制造时固定的,比如iPhone6的屏幕有750x1334个物理像素。
css像素(设备独立像素)
css像素是逻辑像素,是一个抽象的概念,实际显示大小会受到设备像素比的影响。
设备像素比DPR
DPR表示物理像素与css像素的比例关系。如DPR=2时,1个css像素对应横纵各2个物理像素。例如iPhone6的屏幕宽度是375css像素,但实际有750物理像素,所以他的DPR是2。 开发时可以通过 window.devicePixelRatio 获取当前设备的DPR值。
问题1: 设备的DPR是固定的吗?
-
大多数情况下是固定的:对于特定型号的设备,DPR是制造商设定好的硬件属性
-
特殊情况会发生变化:
- 用户调整系统显示缩放比例时
- 浏览器的缩放功能被使用时
- 某些设备在横竖屏切换时可能会有不同的表现
视口viewport
viewport有3种类型
-
布局视口
- 这是浏览器用来放置网页的虚拟区域
- 默认宽度通常是980px,不同的浏览器可能略有差异
- 这就是为什么移动端访问桌面网站时,页面看起来很小的原因
- 可以通过
document.documentElement.clientWidth获取其宽度
-
视觉视口
- 这是用户当前实际看到的区域
- 当用户缩放页面时,视觉视口会改变大小,用户放大时,视觉视口变小,用户缩小时,视觉视口变大
- 可以通过
window.innerWidth获取其宽度
-
理想视口* *
- 这是设备理想的视口宽度,等于设备屏幕的宽度,固定不变
- 通过设置
<meta name="viewport" content="width=device-width">来实现 - 在这种模式下,1个css像素等于1个设备独立像素
- 可以通过
screen.width获取其宽度
移动端适配方案
媒体查询
媒体查询是一种较为传统的适配方案,通过css的@media规则针对不同屏幕尺寸编写不同样式。
@media screen and (min-width:320px) and (max-width:767px){
/**针对小屏幕设备的样式 */
}
@media screen and (min-width: 768px) and (max-width: 1023px){
/**针对中等屏幕设备的样式 */
}
@media screen and (min-width:1024px){
/**针对较大屏幕设备的样式 */
}
/* 设备像素比 */
@media screen and (-webkit-min-device-pixel-ratio: 2) { }
@media screen and (min-resolution: 2dppx) { }
/* 高分辨率屏幕 */
@media screen and (-webkit-min-device-pixel-ratio: 1.5) { }
✅优点:
- 学习成本低
- 兼容性好,几乎所有浏览器都支持
- 可以针对特定设备进行适配
❌缺点:
- 需要写大量媒体查询代码,维护困难
- 设备尺寸变化时,样式切换生硬,不够平滑
- 难以做到精准匹配,只能适配几个关键节点
- 代码冗余较多,每个元素都要在不同断点下重复定义
💡建议: 媒体查询更适合做宏观布局调整,如导航栏折叠,而非元素级精细适配
rem适配
rem是相对于根元素的字体大小单位。rem适配方案的核心思想是动态设置根元素的字体大小,从而实现页面元素的等比例缩放。
实现方式
- 手动设置
/* 设计稿为375px宽度,将其分为10份,每份37.5px */
html {
font-size:37.5px;
}
.header {
width: 10rem; /** 375px / 37.5px = 10rem */
height: 2.67rem; /**100px / 37.5px = 2.67rem */
}
/** 设计稿为750px宽度,将其分为10份,每份75px */
html{
font-size:75px;
}
.header {
width: 10rem; /**750px / 75px = 10rem */
height: 2.67rem; /**200px / 75px = 2.67rem */
}
- 动态计算
(function(){
function setRootFontSize(){
const screenWidth = document.documentElement.clientWidth || document.body.clientWidth;
const rootFontSize = screenWidth / 10;
document.documentElement.style.fontSize = rootFontSize + 'px';
}
setRootFontSize();
// 窗口大小改变重新调整
window.addEventListener('resize', setRootFontSize);
// 屏幕旋转重新调整
window.addEventListener('orientationchange', setRootFontSize);
})();
✅优点
- 实现简单,适配效果好
- 保持设计稿还原度
- 一套代码适配多种屏幕尺寸
- 结合插件可自动转换px为rem
❌缺点
- 需要引入额外的js代码
- 需要使用构建工具插件
- 某些场景会出现精度问题
- 对于固定尺寸如1px边框处理复杂
vw/vh适配方案
1vw = 视口宽度的1% 100vw = 设备屏幕宽度 1vh = 视口高度的1% 100vh = 设备的屏幕高度
实现方式
- 设置
<meta name="viewport" content="width=device-width, initial-scale=1.0">直接使用vw/vh单位 - 配合postcss-px-to-viewport插件使用,可以在开发中直接写px,插件会自动转换为vw
// postcss.config.js
module.exports = {
plugins: {
'postcss-px-to-viewport': {
viewportWidth: 375, // 设计稿宽度
viewportHeight: 667, // 设计稿高度
unitPrecision: 5, // 保留小数位数
viewportUnit: 'vw', // 转换的单位
selectorBlackList: ['.ignore'], // 忽略的类名
minPixelValue: 1, // 小于1px不转换
mediaQuery: false, // 媒体查询中的px不转换
exclude: [/node_modules/] // 忽略的文件
}
}
};
✅优点
- 简单直接,不需要使用js计算,纯css实现
- 维护成本低,代码简洁,易于理解和维护
- 适配精确,严格按照屏幕比例缩放
❌缺点
- 高度适配复杂,vh在不同设备上差异过大
- 字体大小变化,在大屏设备上字体可能过大
- 1px边框问题,小屏幕设备上可能过细
优化方案
- 限制最大最小值
/* 限制最大最小值 */
.title {
font-size: clamp(16px, 4vw, 24px); /* 最小16px,最大24px */
}
/* 使用媒体查询优化 */
@media screen and (min-width: 768px) {
.container {
width: 750px; /* 大屏设备固定宽度 */
margin: 0 auto;
}
}
- vw和rem混合使用
/* 根字体大小使用vw */
html {
font-size: 4vw; /* 基准字体大小 */
}
/* 其他元素使用rem结合vw的优势 */
.header {
font-size: 1.2rem; /* 相对于根字体 */
height: 3.75rem; /* 固定比例 */
}
/* 特殊元素直接使用vw */
.banner {
width: 100vw; /* 全屏宽度 */
height: 30vw; /* 固定宽高比 */
}
⚠️ 使用rem或vw/vh适配需要设置
<meta name="viewport" content="width=device-width, initial-scale=1.0">🎯推荐组合: + vw/vh(主) + clamp()(限幅) + 媒体查询(大屏回退)