移动端适配终极指南:rem 原理与实战

各位前端大佬们,今天我们来聊聊移动端开发中绕不开的话题------rem 适配。无论你是刚入行的新人,还是有一定经验的开发者,掌握 rem 适配都能让你在开发多端页面时游刃有余。本文将从原理到实战,带你彻底搞懂 rem,并附上可直接运行的示例代码。

一、为什么要做移动端适配?

现在的手机屏幕尺寸五花八门:iPhone SE(375px)、iPhone 14 Pro Max(430px)、各种安卓机型(360px、390px、412px......)。如果都用固定的 px 单位,就会出现:在大屏上元素显得太小,在小屏上又挤在一起。

我们需要一种机制,让页面元素根据屏幕宽度自动缩放,在不同设备上保持视觉比例一致。这就是移动端适配要解决的核心问题。

二、rem 是什么?

rem (root em)是一个相对单位,它相对于 HTML 根元素的 font-size

例如:

css

css 复制代码
html { font-size: 16px; }
h1 { font-size: 2rem; }  /* 实际 = 32px */
p  { font-size: 1rem; }  /* 实际 = 16px */

如果根字体变成 20px,所有 rem 值都会等比例放大。

与 px、em 的区别

  • px:绝对单位,大小固定,不随环境变化。
  • em :相对单位,相对于父元素字体大小,容易嵌套混乱。
  • rem:相对单位,始终相对于根元素,简单可控。

rem 非常适合用来构建可缩放的布局。

三、rem 适配的原理

核心思想:动态改变根元素的字体大小,所有使用 rem 的元素自动缩放

公式如下:

text

scss 复制代码
根字体大小 = (当前屏幕宽度 / 设计稿宽度) * 基准值
  • 设计稿宽度:通常是 750px(iPhone 6/7/8 二倍图)或 375px。
  • 基准值:为了方便计算,一般取 100px,这样设计稿上的 100px 在代码里就是 1rem。

举例:设计稿 750px,基准 100px。

屏幕宽 375px 时:根字体 = (375 / 750) * 100 = 50px。

此时 1rem = 50px,设计稿上一个宽 200px 的元素,在代码里写 2rem,实际渲染为 100px,完美缩放到一半。

四、实战:手写一个 rem 适配示例

我们来实现一个简单的商品卡片,让它随屏幕宽度等比缩放。

1. HTML 结构

html

xml 复制代码
<!DOCTYPE html>
<html lang="zh">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no">
  <title>rem 适配实战</title>
  <style>
    /* 样式稍后添加 */
  </style>
</head>
<body>
  <div class="card">
    <img src="https://via.placeholder.com/300" alt="商品图" class="card-img">
    <h3 class="card-title">复古运动鞋</h3>
    <p class="card-price">¥299</p>
    <button class="card-btn">立即购买</button>
  </div>
  <script src="rem.js"></script>
</body>
</html>

2. 动态设置根字体(rem.js)

javascript

ini 复制代码
// rem.js
(function (designWidth = 750, baseSize = 100) {
  function setRem() {
    const width = document.documentElement.clientWidth;
    // 限制最大宽度,防止在平板或电脑上过大
    const maxWidth = 750;
    const rem = (Math.min(width, maxWidth) / designWidth) * baseSize;
    document.documentElement.style.fontSize = rem + 'px';
  }

  setRem();
  window.addEventListener('resize', setRem);
  window.addEventListener('pageshow', function (e) {
    if (e.persisted) setRem();
  });
})();

将这段代码保存为 rem.js 并引入页面。它会在页面加载和窗口大小变化时重新计算根字体。

3. 编写 CSS(使用 rem)

设计稿尺寸:卡片宽 340px,图片 300x300px,标题字体 28px,价格字体 32px,按钮高 70px,圆角 16px。基准 100px,所以转换为 rem:

css

css 复制代码
* {
  margin: 0;
  padding: 0;
  box-sizing: border-box;
}

body {
  background: #f5f5f5;
  display: flex;
  justify-content: center;
  align-items: center;
  min-height: 100vh;
  font-family: sans-serif;
}

.card {
  width: 3.4rem;        /* 340px / 100 */
  background: white;
  border-radius: 0.2rem; /* 20px / 100 */
  padding: 0.2rem;       /* 20px / 100 */
  box-shadow: 0 0.04rem 0.1rem rgba(0,0,0,0.1);
  text-align: center;
}

.card-img {
  width: 100%;
  height: auto;
  aspect-ratio: 1/1;
  border-radius: 0.16rem; /* 16px / 100 */
}

.card-title {
  font-size: 0.28rem;    /* 28px / 100 */
  margin: 0.2rem 0 0.1rem;
}

.card-price {
  font-size: 0.32rem;    /* 32px / 100 */
  color: #ff4400;
  font-weight: bold;
  margin-bottom: 0.2rem;
}

.card-btn {
  height: 0.7rem;        /* 70px / 100 */
  width: 100%;
  background: #ff4400;
  color: white;
  border: none;
  border-radius: 0.35rem; /* 35px / 100 */
  font-size: 0.28rem;
  cursor: pointer;
}

4. 效果验证

在 Chrome 开发者工具中切换不同手机型号,观察卡片大小变化。你会发现卡片始终按设计稿比例缩放,完美适配各种屏幕。


五、工程化实践:自动转换 px 到 rem

手算 rem 太麻烦?我们可以使用 postcss-pxtorem 插件,在开发时直接写设计稿的 px,编译后自动转为 rem。

1. 安装依赖

bash

css 复制代码
npm install postcss-pxtorem --save-dev

2. 配置 postcss.config.js

js

java 复制代码
module.exports = {
  plugins: {
    'postcss-pxtorem': {
      rootValue: 100,           // 基准值,必须与 JS 中的基准一致
      propList: ['*'],          // 所有属性都转换
      selectorBlackList: [],    // 忽略的选择器
      minPixelValue: 2,         // 小于 2px 的不转换(保留 px)
    }
  }
}

3. 编写 CSS 时直接写 px

css

css 复制代码
.card {
  width: 340px;   /* 开发时直接写 px */
  background: white;
  border-radius: 20px;
  padding: 20px;
}
/* 编译后会自动变成 rem */

配合构建工具(Webpack、Vite),开发体验大大提升。


六、注意事项与优化

  1. 字体处理
    有些设计希望正文字体在手机上保持合适大小,不随缩放变得过大。可以结合媒体查询对字体单独用 px 控制,或者设置一个范围。
  2. 1px 边框问题
    在 Retina 屏上,1px 可能显示为 2px 物理像素。可以用 transform: scale(0.5) 或直接保留 1px(通过 minPixelValue 不转换)。
  3. 图片适配
    使用 img 标签时,设置容器宽高比(aspect-ratio),并准备 2x、3x 图,通过 srcset 适配不同分辨率。
  4. 限制最大宽度
    在平板或电脑上,页面可能过宽。可以在 JS 中限制最大宽度(如 750px),或在 CSS 中给根元素或外层容器设置 max-width: 750px; margin: 0 auto;,让内容居中。
  5. 避免字体过大
    如果根字体变得很大(例如屏幕宽 750px 时根字体 100px),页面内文字会非常大。通常我们会限制最大根字体,或者对文字单独用 vw 单位控制。

七、总结

rem 适配是移动端开发的经典方案,通过动态根字体 + rem 单位实现页面等比缩放。它的优点:

  • 原理简单,兼容性好。
  • 配合自动化工具,开发效率高。
  • 能平滑适应各种屏幕宽度。

当然,现在也有了 vw/vh 方案,可以直接用视口单位实现类似效果。但 rem 方案仍有其不可替代的场景(如需要整体缩放字体、与第三方库配合等)。掌握 rem,你就能在移动端适配中游刃有余。

希望这篇文章对你有帮助!如果你有任何疑问,欢迎在评论区留言交流。别忘了点赞收藏,我们下期见!


附:完整示例代码

你可以将以下代码保存为一个 HTML 文件,直接运行查看效果。

html

xml 复制代码
<!DOCTYPE html>
<html lang="zh">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no">
  <title>rem 适配示例</title>
  <style>
    * { margin: 0; padding: 0; box-sizing: border-box; }
    body {
      background: #f5f5f5;
      display: flex;
      justify-content: center;
      align-items: center;
      min-height: 100vh;
      font-family: sans-serif;
    }
    .card {
      width: 3.4rem;
      background: white;
      border-radius: 0.2rem;
      padding: 0.2rem;
      box-shadow: 0 0.04rem 0.1rem rgba(0,0,0,0.1);
      text-align: center;
    }
    .card-img {
      width: 100%;
      height: auto;
      aspect-ratio: 1/1;
      border-radius: 0.16rem;
      background: #ddd;
    }
    .card-title {
      font-size: 0.28rem;
      margin: 0.2rem 0 0.1rem;
    }
    .card-price {
      font-size: 0.32rem;
      color: #ff4400;
      font-weight: bold;
      margin-bottom: 0.2rem;
    }
    .card-btn {
      height: 0.7rem;
      width: 100%;
      background: #ff4400;
      color: white;
      border: none;
      border-radius: 0.35rem;
      font-size: 0.28rem;
      cursor: pointer;
    }
  </style>
</head>
<body>
  <div class="card">
    <div class="card-img"></div>
    <h3 class="card-title">复古运动鞋</h3>
    <p class="card-price">¥299</p>
    <button class="card-btn">立即购买</button>
  </div>
  <script>
    (function (designWidth = 750, baseSize = 100) {
      function setRem() {
        const width = document.documentElement.clientWidth;
        const maxWidth = 750;
        const rem = (Math.min(width, maxWidth) / designWidth) * baseSize;
        document.documentElement.style.fontSize = rem + 'px';
      }
      setRem();
      window.addEventListener('resize', setRem);
    })();
  </script>
</body>
</html>

在浏览器中打开,调整窗口大小,观察卡片如何平滑缩放。这就是 rem 适配的魅力!

相关推荐
user86158185781542 小时前
彻底解决 Dart Sass 升级导致的 @import 弃用警告及 Vite 缓存踩坑实录
前端
青青家的小灰灰2 小时前
Pinia 完全指南:重构你的 Vue 3 状态管理架构
前端·javascript·vue.js
yuki_uix2 小时前
深入理解 JavaScript Event Loop:从概念到实践的完整探索
前端·javascript
程序员阿峰2 小时前
WebSocket 原理解析
前端
Lee川2 小时前
JavaScript 继承进化史:从原型链的迷雾到完美的寄生组合
前端·javascript·面试
米饭同学i2 小时前
微信小程序实现故事线指引动画效果
前端
阿懂在掘金2 小时前
为什么写 Vue 强烈建议用 Setup?除了复用,更是代码组织
前端·vue.js
sorryhc2 小时前
我让 AI 帮我写了一个 Code Agent!
前端·openai·ai编程
工边页字2 小时前
面试官:请详细介绍下AI中的token,越详细越好!
前端·人工智能·后端