Skip to content

移动端适配

移动端适配的核心目标是让网页在不同尺寸、分辨率的设备上呈现一致的视觉效果和用户体验。其核心原理是通过动态调整布局和元素尺寸,适应不同设备的屏幕特性。

以下是实现移动端适配的关键方法和原理:

一、核心适配方案

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>)的字体大小动态计算尺寸。

    • 设置基准值
    js
    document.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% */

二、适配原理总结

  1. 视口标准化:通过 <meta viewport> 确保页面宽度与设备物理宽度一致。

  2. 相对单位替代固定像素:使用 rem、vw 等单位,使元素尺寸随屏幕动态变化。

  3. 弹性布局:通过 Flexbox/Grid 实现自适应排列,避免固定宽度布局。

  4. 媒体查询分段适配:针对不同屏幕范围调整样式细节(如字体大小、间距)。

三、方案对比

方案优点缺点
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);
}