移动端适配方案

8 阅读3分钟

meta viewport

首先要给项目的 index.html 添加 meta viewport:

<meta name="viewport" content="width=device-width,initial-scale=1,minimum-scale=1,maximum-scale=1,user-scalable=no,viewport-fit=cover">

rem 适配方案

核心原理

  • 根据屏幕宽度,使用 JS 动态设置根元素(html)的 font-size,让 rem 单位随屏幕宽度等比例变化。
  • 开发时写 px 单位,通过 PostCSS 插件自动转换为 rem

实现步骤

  1. 安装并配置 PostCSS,将 px 转成 rem

    npm install -D postcss postcss-pxtorem
    
    // postcss.config.ts
    export default {
      plugins: {
        'postcss-pxtorem': {
          rootValue: 37.5, // 设计稿宽度的 1/10(375px/10=37.5)
          propList: ['*'], // 需要转换的 CSS 属性,* 表示全部
          exclude: /node_modules/i, // 排除第三方库(如 UI 组件库)
          minPixelValue: 1 // 最小转换像素值,小于 1px 不转换
        }
      }
    }
    
  2. 动态设置根元素字体大小。

    方式一:手写 JS 计算逻辑。

    // 动态计算并设置 html 的 font-size
    function setRemUnit() {
      const docEl = document.documentElement;
      // 获取视口宽度,限制最大宽度避免大屏设备元素过大
      const width = Math.min(docEl.clientWidth, 750);
      docEl.style.fontSize = (width / 10) + 'px';
    }
    
    // 初始化执行
    setRemUnit();
    
    // 屏幕旋转、窗口大小变化时重新计算
    window.addEventListener('resize', setRemUnit);
    
    // 页面从后台切换到前台时重新计算
    window.addEventListener('pageshow', (e) => {
      if (e.persisted) setRemUnit();
    });
    

    方式二:使用 lib-flexible

    npm install amfe-flexible
    
    // main.ts 引入
    import 'amfe-flexible/index.js'
    

vw 适配方案

核心原理

  • 使用 CSS 原生支持的视口单位(vw),纯 CSS 实现适配。
  • 开发时写 px,通过 PostCSS 自动转换为 vw

实现步骤

安装并配置 PostCSS,将 px 转换为 vw

npm install -D postcss-px-to-viewport
// postcss.config.ts
export default {
  plugins: {
    'postcss-px-to-viewport': {
      unitToConvert: 'px', // 需要转换的单位(默认 px)
      viewportWidth: 375, // 设计稿宽度(必须与设计稿一致)
      unitPrecision: 6, // 转换后的精度(保留 6 位小数)
      propList: ['*'], // 需要转换的 CSS 属性
      viewportUnit: 'vw', // 目标转换单位
      fontViewportUnit: 'vw', // 字体的目标转换单位
      selectorBlackList: ['.ignore', '.hairlines'], // 忽略转换的类名
      minPixelValue: 1, // 最小转换像素值
      mediaQuery: false, // 是否转换媒体查询中的 px
      exclude: /node_modules/i, // 排除第三方库
      landscape: false // 是否处理横屏场景
    }
  }
}

1px 边框问题

原因:物理像素(屏幕上的实际像素点)与 CSS 像素(写 CSS 时的 px 单位)不匹配。

  • 在普通屏(dpr = 1)上:1 个 CSS 像素 = 1 个物理像素,视觉上 1px 就是 1px 的边框。
  • 在高清屏(dpr = 2)上:1 个 CSS 像素 = 2 个物理像素,视觉上 1px 就是 2px 的边框。

dpr 是“设备像素比”,dpr = 物理像素宽度 / CSS 像素宽度

如 iPhone 6/7/8(Retina 屏),dpr = 750(物理宽度)/ 375(CSS 宽度)= 2

实现:利用 伪元素 + 缩放 实现 1px 边框。

.card {
  position: relative;

  &::after {
    content: '';
    position: absolute;
    width: 200%;
    height: 200%;
    border: 1px solid red;
    transform: scale(0.5);
    transform-origin: 0 0;
  }
}

大屏适配限制

现象:由于页面元素大小会根据屏幕宽度动态缩放,导致在平板等大屏幕设备上页面元素显示得很大,不利于美观和使用的便利性。

解决方案:可配合使用媒体查询固定页面最大宽度(如 max-width: 750px),rem 方案还需要限制根元素字体大小,避免元素过大。

@media (min-width: 750px) {
  html {
    // !important 覆盖 JS 动态设置
    font-size: 75px !important;
  }

  #root {
    max-width: 750px;
    margin: 0 auto;
  }
}