Appearance
移动端适配
移动端适配的核心目标是让网页在不同尺寸、分辨率的设备上呈现一致的视觉效果和用户体验。其核心原理是通过动态调整布局和元素尺寸,适应不同设备的屏幕特性。
以下是实现移动端适配的关键方法和原理:
一、核心适配方案
1. Viewport 设置
作用:控制视口(浏览器可视区域)的行为,确保页面宽度与设备屏幕宽度一致,并禁止用户缩放。
代码示例:
html
<meta
name="viewport"
content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no"
/>- 原理:
- width=device-width:将视口宽度设置为设备屏幕宽度(如 iPhone 12 的 390px)。
- initial-scale=1.0:初始缩放比例为 1,避免页面被放大或缩小。
- 禁止缩放(user-scalable=no)可防止用户手动调整布局。
2. 响应式布局(Responsive Design)
方法:使用 CSS 媒体查询(Media Queries)根据屏幕宽度分段调整样式。
代码示例:
css
/* 默认样式(移动端优先) */
.container {
width: 100%;
}
/* 平板及以上设备 */
@media (min-width: 768px) {
.container {
width: 750px;
margin: 0 auto;
}
}
/* 桌面端 */
@media (min-width: 1024px) {
.container {
width: 960px;
}
}- 原理:通过断点(Breakpoints)检测屏幕宽度,动态应用不同的 CSS 规则。
3. 弹性布局(Flexbox)与网格布局(Grid)
作用:替代固定宽度的布局,使用相对单位(如百分比、flex、grid)实现自适应。
示例:
css
.flex-container {
display: flex;
flex-wrap: wrap; /* 允许换行 */
}
.flex-item {
flex: 1 1 30%; /* 弹性基准值 30%,可伸缩 */
}4. REM 与 VW 单位
REM:基于根元素(
<html>)的字体大小动态计算尺寸。- 设置基准值:
jsdocument.documentElement.style.fontSize = window.innerWidth / 10 + 'px' // 例如 375px 屏幕 → 37.5px- CSS 使用:
js.box { width: 3.75rem; } /* 3.75rem = 37.5px(适配 375px 屏幕) */- 原理:通过 JS 动态调整 rem 基准值,使元素尺寸随屏幕宽度等比缩放。
VW/VH:基于视口宽度的百分比单位。
1vw = 视口宽度的 1%,1vh = 视口高度的 1%。
示例:
css.box { width: 50vw; } /* 宽度始终为屏幕宽度的 50% */
二、适配原理总结
视口标准化:通过
<meta viewport>确保页面宽度与设备物理宽度一致。相对单位替代固定像素:使用 rem、vw 等单位,使元素尺寸随屏幕动态变化。
弹性布局:通过 Flexbox/Grid 实现自适应排列,避免固定宽度布局。
媒体查询分段适配:针对不同屏幕范围调整样式细节(如字体大小、间距)。
三、方案对比
| 方案 | 优点 | 缺点 |
|---|---|---|
| REM + JS 动态计算 | 精准控制缩放比例,适配复杂布局 | 需引入 JS 计算,可能影响性能 |
| VW 单位 | 纯 CSS 实现,无需 JS | 对高度适配不友好,部分场景兼容性差 |
| 媒体查询 | 灵活控制不同断点的样式细节 | 需手动编写多套样式,维护成本较高 |
四、具体实现
这里采用 rem 转换 + 淘宝适配 来实现
rem转换:postcss-pxtorem:它可以自动将 px 转化成 rem, 也就意味着我们可以不再使用之前自己写的转化了,在 css 中直接写 px 就可以了amfe-flexible:amfe-flexible 会自动根据设备的屏幕宽度来调整 font-size(源码:https://www.npmjs.com/package/amfe-flexible?activeTab=code)
1、在入口文件引入
ts
// main.ts
import 'amfe-flexible'原理:监听窗口的大小变化,通过设置 html 根元素的字体大小,来实现 REM 自适应布局(可以在 vue 的 html 项目中直接写入)
document.documentElement.clientWidth / 10 ,例如:当设计稿为 750px , rem = 750 / 10 = 75px ,所以在配置 px 转 rem 的时候( pxtorem 插件),rootValue 配置的是 75。
js
;(function flexible(window, document) {
function setRemUnit() {
// 源码中就是将设计稿750/10
var rem = document.documentElement.clientWidth / 10
// 给一个最大值
if (rem > 60) rem = 60
document.documentElement.style.fontSize = rem + 'px'
}
setRemUnit()
window.addEventListener('resize', setRemUnit)
})(window, document)2、配置 Vite 来使用 PostCSS 插件
rootValue: 75 表示 1rem = 75px
js
import pxtorem from 'postcss-pxtorem'
import { defineConfig } from 'vite'
export default defineConfig(({ mode }) => {
return {
// ...
css: {
postcss: {
plugins: [
pxtorem({
rootValue: 75, // 根元素 (html) 的字体大小
propList: ['*', '!--van*'] // 过滤vant定义的--van开头的属性的px转化
})
]
}
}
}
})3、IOS 底部安全区适配
对于 ios 系统,在 X 以后的机型底部会有”底部指示器“(也就是那个白色的条),在布置页面时,如果按钮在底部,就会被遮挡,影响用户交互体验,可以设置 constant 和 env 函数,参数为 safe-area-inset-x 解决
env()和constant(),是 IOS11 新增特性,Webkit 的 css 函数,用于设定安全区域与边界的距离,有 4 个预定义变量
safe-area-inset-left:安全区域距离左边边界的距离
safe-area-inset-right:安全区域距离右边边界的距离
safe-area-inset-top:安全区域距离顶部边界的距离
safe-area-inset-bottom :安全距离底部边界的距离
为了方便复用,拿 sass 代码举例:
scss
@mixin safe-padding-top($x: 0px) {
@if $x== 0px {
padding-top: constant(safe-area-inset-top);
padding-top: env(safe-area-inset-top);
} @else {
padding-top: calc(#{$x} + constant(safe-area-inset-top));
padding-top: calc(#{$x} + env(safe-area-inset-top));
}
}
@mixin safe-padding-bottom($x: 0px) {
// ipone底部横条处理
@if $x== 0px {
padding-bottom: constant(safe-area-inset-bottom);
padding-bottom: env(safe-area-inset-bottom);
} @else {
padding-bottom: calc(#{$x} + constant(safe-area-inset-bottom));
padding-bottom: calc(#{$x} + env(safe-area-inset-bottom));
}
}
@mixin safe-bottom($x: 0px) {
@if $x== 0px {
bottom: constant(safe-area-inset-bottom);
bottom: env(safe-area-inset-bottom);
} @else {
bottom: calc(#{$x} + constant(safe-area-inset-bottom));
bottom: calc(#{$x} + env(safe-area-inset-bottom));
}
}
@mixin safe-margin-bottom($x: 0px) {
@if $x== 0px {
margin-bottom: constant(safe-area-inset-bottom);
margin-bottom: env(safe-area-inset-bottom);
} @else {
margin-bottom: calc(#{$x} + constant(safe-area-inset-bottom));
margin-bottom: calc(#{$x} + env(safe-area-inset-bottom));
}
}使用:
scss
.container {
color: blue;
@include safe-padding-bottom(0);
}