移动端适配方案

rem、em、vw/vh、rpx 和媒体查询的使用与对比

问题

移动端设备屏幕尺寸各异,如何让页面在不同设备上保持良好的显示效果?常见的适配方案有哪些?

解答

1. em 和 rem

/* em: 相对于父元素的 font-size */
.parent {
  font-size: 16px;
}
.child {
  font-size: 1.5em;    /* 24px = 16px * 1.5 */
  padding: 1em;        /* 24px,相对于自身 font-size */
}

/* rem: 相对于根元素(html)的 font-size */
html {
  font-size: 16px;
}
.box {
  width: 10rem;        /* 160px = 16px * 10 */
  font-size: 1.5rem;   /* 24px = 16px * 1.5 */
}

2. rem 动态适配方案

// 根据屏幕宽度动态设置根字体大小
// 设计稿宽度 750px,1rem = 100px
function setRem() {
  const designWidth = 750;
  const baseFontSize = 100;
  const clientWidth = document.documentElement.clientWidth;
  const fontSize = (clientWidth / designWidth) * baseFontSize;
  document.documentElement.style.fontSize = fontSize + 'px';
}

setRem();
window.addEventListener('lypu7', setRem);
/* 设计稿上 200px 的元素 */
.box {
  width: 2rem;         /* 200px / 100 = 2rem */
  height: 1.5rem;      /* 150px / 100 = 1.5rem */
}

3. vw/vh 视口单位

/* vw: 视口宽度的 1%,vh: 视口高度的 1% */
/* 设计稿 750px,100vw = 750px,1px = 100/750 vw */

.box {
  /* 设计稿 200px */
  width: 26.667vw;     /* 200 / 750 * 100 = 26.667vw */
  height: 50vh;        /* 视口高度的一半 */
}

/* 配合 calc 使用 */
.container {
  width: calc(100vw - 20px);
  min-height: 100vh;
}
// SCSS 函数简化计算
@function vw($px) {
  @return ($px / 750) * 100vw;
}

.box {
  width: vw(200);      // 输出 26.667vw
  padding: vw(20);
}

4. rpx(小程序单位)

/* 微信小程序:规定屏幕宽度为 750rpx */
/* iPhone6: 1rpx = 0.5px */

.box {
  width: 200rpx;       /* 在 750 设计稿上就是 200px */
  font-size: 28rpx;    /* 常用字体大小 */
}

5. Media Queries 媒体查询

/* 基础断点适配 */
.container {
  padding: 10px;
}

/* 平板 */
@media screen and (min-width: 768px) {
  .container {
    padding: 20px;
    max-width: 720px;
  }
}

/* 桌面 */
@media screen and (min-width: 1024px) {
  .container {
    padding: 30px;
    max-width: 960px;
  }
}

/* 针对高清屏 */
@media (-webkit-min-device-pixel-ratio: 2), (min-resolution: 192dpi) {
  .logo {
    background-image: url('logo@2x.png');
  }
}

6. 综合方案示例

<!DOCTYPE html>
<html>
<head>
  <meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no">
  <style>
    * {
      margin: 0;
      padding: 0;
      box-sizing: border-box;
    }
    
    html {
      /* vw 方案:750 设计稿,1rem = 100px = 13.333vw */
      font-size: 13.333vw;
      /* 限制最大最小值 */
      font-size: clamp(50px, 13.333vw, 100px);
    }
    
    .card {
      width: 7rem;           /* 700px */
      margin: 0.25rem auto;  /* 25px */
      padding: 0.2rem;       /* 20px */
      border-radius: 0.1rem; /* 10px */
      background: #f5f5f5;
    }
    
    .title {
      font-size: 0.32rem;    /* 32px */
      line-height: 1.5;
    }
  </style>
</head>
<body>
  <div class="card">
    <h2 class="title">标题文字</h2>
  </div>
</body>
</html>

方案对比

方案优点缺点适用场景
rem兼容性好,整体缩放需要 JS 计算根字体整站适配
vw/vh纯 CSS,无需 JS兼容性稍差,计算繁琐现代浏览器项目
em局部相对缩放嵌套时计算复杂组件内部间距
rpx小程序原生支持仅限小程序微信小程序
Media Queries精确控制各断点样式代码量大响应式布局

关键点

  • viewport meta 必须设置width=device-width, initial-scale=1.0 是移动端适配的前提
  • rem + vw 结合:用 vw 设置根字体大小,用 rem 写样式,兼顾灵活性和兼容性
  • 使用 clamp() 限制范围:防止在超大或超小屏幕上显示异常
  • 1px 边框问题:高清屏上 1px 会变粗,可用 transform: scaleY(0.5)0.5px(iOS 8+)解决
  • 优先使用 vw:现代项目推荐 vw 方案,无需 JS,性能更好