Skip to content

移动端适配与 rem(一) ​

本页对应 移动端适配与 rem,共 7 段视频,约 52 分钟。

学习目标 ​

  • 能用自己的话解释移动适配的含义,并举例说明同一元素在不同移动设备上显示尺寸不同的原因。
  • 能使用 Chrome 开发者工具打开设备模拟器,切换 iPhone 5、iPhone 6/7/8、iPhone 6/7/8 Plus,完成页面刷新并调整调试面板位置。
  • 能区分物理分辨率与逻辑分辨率,准确说出 iPhone 5、iPhone 6/7/8、iPhone 6/7/8 Plus 的逻辑分辨率。
  • 能解释移动端网页默认按 980px 显示导致页面缩小的原因,并为页面添加正确的 viewport meta 标签。
  • 能识别二倍图设计稿,在 PSD 或网页设计稿中将缩放/像素密度校准为 2 倍后再量取尺寸。
  • 能说明 rem 作为移动端适配方案的基本原理,即长度单位相对于根元素字体大小进行计算。

知识点 ​

谷歌模拟器 ​

谷歌模拟器界面

为什么需要: 移动端页面最终要在手机、平板上看,但写代码时不可能一直插数据线用真机测试,所以先用谷歌浏览器模拟移动设备,方便查看页面效果。
怎么想: 模拟器只是把浏览器的“可视区域”切成手机尺寸;它不改变代码,改变的是查看环境。转写里反复强调:切换设备后页面要刷新,否则你看到的还可能像 PC 页面。
怎么做: 在页面空白处右键 →“检查”打开控制台 → 点控制台左上角的设备切换图标 → 选择 iPhone 5、iPhone 6/7/8、iPhone 6/7/8 Plus 等设备。建议把调试栏放到左右两侧,避免挤占设备宽度;缩放比例保持 100%,不要用 Ctrl+滚轮 缩放浏览器。找不到设备时,在设备下拉最底部点 Edit 勾选需要的机型;还可以切换横屏/竖屏。
易错点: 切换设备后忘记刷新;调试栏放在下方把设备“挤小”;用浏览器缩放代替设备缩放,导致尺寸判断不准;把模拟器当成真机,忽略不同设备宽度不同。

屏幕分辨率 ​

物理分辨率与逻辑分辨率示意图

为什么需要: 不同手机屏幕大小不同,同一个元素在不同设备上显示大小也不同,这就是移动适配要解决的问题。写页面前必须知道以哪个“分辨率”为参照。
怎么想: 分辨率分两类:物理分辨率是硬件分辨率,出厂就固定;逻辑分辨率是软件/驱动设置后的分辨率。转写里用电脑举例:物理 1920×1080,在 100% 缩放下逻辑分辨率也是 1920×1080,看不出区别;调到 125% 后,逻辑宽度约为 1920 ÷ 1.25 = 1536,页面大小就跟着逻辑分辨率变了。结论是:制作网页参考逻辑分辨率。
怎么做: 记住常用移动设备逻辑分辨率:iPhone 5 为 320×568;iPhone 6/7/8 为 375×667;iPhone 6/7/8 Plus 为 414×736。最需要先记住 iPhone 6/7/8 的宽度 375,因为设计师常以它为设计稿基准。
易错点: 把物理分辨率和逻辑分辨率混用;记成“页面按物理分辨率写”;只记高度不记宽度。实际沟通中常以宽度判断设备适配基准。

视口 ​

为什么需要: 视口是显示 HTML 网页的区域,用来约束 HTML 的尺寸。转写里演示:不加视口标签时,移动浏览器默认按约 980px 的宽度布局网页,再塞进 375px 左右的设备里,页面只能被缩小,字很小、没法看。
怎么想: 要让“视口宽度”和“设备宽度”一致,比例设为 1:1,这样页面才能按设备宽度正常显示。
怎么做: 在 head 中加入视口标签:

html
<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta http-equiv="X-UA-Compatible" content="IE=edge">

  <!– 视口标签 -->
  <meta name="viewport" content="width=device-width, initial-scale=1.0">

  <title>Document</title>
</head>
<body>
  
</body>
</html>

其中 width=device-width 表示视口宽度等于设备宽度;initial-scale=1.0 表示缩放 1 倍、不缩放。实际开发中这句话通常由编辑器骨架自动生成,但要知道它的作用。
易错点: 漏写视口标签导致页面被整体缩小;把 initial-scale 写成其他值造成缩放异常;以为加了视口标签就完成所有适配,它只解决视口与设备宽度对齐问题。

二倍图 ​

二倍图使用方法示意图

为什么需要: 设计稿如果只按 375px 做一倍图,图片放到更大或更高分辨率的屏幕下容易被放大而模糊失真。二倍图就是设计稿中元素尺寸按设备尺寸的 2 倍来做,防止图片在高分辨率屏幕下模糊失真。
怎么想: 以 iPhone 6/7/8 为例,设备逻辑宽度 375,二倍设计稿宽度通常是 750。你在设计稿上量到 750,不能直接写成 CSS 的 750px,而要按二倍关系换算成 375px。转写中强调:看到设计稿量出来是设备宽度的两倍,第一反应就是“二倍图”。
怎么做: 使用设计稿前先校准。网页版设计稿可在设置里把像素密度改成 2x 再量;PSD 设计稿也要按二倍图关系校准/换算后再量,不能拿设计稿标注值直接当 CSS 像素。写页面时图片资源可按二倍图准备,CSS 尺寸按逻辑尺寸写。
易错点: 直接量 750 就写 750;把二倍图理解成“页面所有 CSS 都乘 2”;只换图片不换尺寸;不校准设计稿就开始量。

适配方案 ​

为什么需要: 移动设备宽度不同,同一个元素在不同设备下要有合适显示,不能只写死一套固定像素。转写里明确:移动适配就是不同设备下同一元素有不同大小显示。
怎么想: 适配分两条线:宽度适配解决“宽度自适应”,如百分比布局、Flex 布局;等比适配解决“宽高等比缩放”,如 rem、vw。今天先建立框架,后面再细讲 rem 和 vw。
怎么做: 做横向排列、宽度分配时优先考虑百分比/Flex;需要整体按屏幕比例缩放时使用 rem/vw。不要把“宽度自适应”和“等比缩放”混为一谈。
易错点: 全用固定 px 导致小屏溢出、大屏留白;以为 Flex 能解决所有等比缩放;忘记二倍图换算;切换设备后不刷新就判断适配效果。

从移动适配理解 rem ​

为什么需要:手机、平板等设备宽度不同,同一个元素如果写死 px,在不同设备上显示大小就会不一样。转写稿里把这件事说得很直接:移动适配就是同一个元素在不同设备下有不同的大小显示。
怎么想:既然设备宽度会变,就不能只依赖固定像素,要让尺寸跟着视口变化。rem 就是为此服务的相对单位。
怎么做:先记住结论:rem 是相对单位,相对于 HTML 标签的字号计算结果,1rem = 1个HTML字号大小。
易错点:rem 不是相对父元素字号,而是相对根节点 html 的字号;不要和 em 混淆。

rem 单位的基本含义 ​

为什么需要:移动端页面要在不同宽度下保持比例,固定 px 无法自动缩放。
怎么想:只要 HTML 根字号会随视口变化,那么用 rem 写的元素尺寸也会随之变化。
怎么做:把设计稿中的 px 换算成 rem,页面中写 rem。例如根字号变化时,2rem 的实际大小也会变化。
易错点:1rem 永远等于当前 html 的字号,不是固定 16px;如果手动改了 html 字号,所有 rem 尺寸都会跟着变。

媒体查询:按视口宽度写差异化样式 ​

为什么需要:不同视口宽度下,可能需要执行不同的 CSS 样式。媒体查询能够检测视口的宽度,然后编写差异化的 CSS 样式。
怎么想:把“某个条件成立”作为开关,条件成立时才执行对应 CSS。
怎么做:例如当视口宽度为 320px 时,把 html 背景改成绿色:

css
@media (width:320px) {
  html {
    background-color: green;
  }
}

媒体查询示意图

易错点:媒体查询检测的是视口宽度,不要理解成设备物理分辨率;在谷歌模拟器里切换设备后要刷新页面再看效果;条件不成立时样式不会执行。

rem 布局:把视口等分成 10 份 ​

为什么需要:如果每个元素都单独算比例,开发会很麻烦,需要一套统一的基准。
怎么想:目前 rem 布局方案中,将网页等分成 10 份,HTML 标签的字号为视口宽度的 1/10。这样 1rem 就代表视口宽度的十分之一。
怎么做:先确定参考设备视口宽度,再算出基准根字号:基准根字号 = 视口宽度 / 10。页面元素按这个基准换算成 rem。
易错点:根字号不是随便写一个固定值,而是跟视口宽度挂钩;不要把“网页等分成 10 份”理解成每个元素固定占一份。

rem布局等分示意图

flexible.js:自动设置根字号 ​

为什么需要:不同视口宽度下都要重新计算 html 的 font-size,手写容易漏、容易错。
怎么想:flexible.js 是手淘开发出的一个用来适配移动端的 js 库,核心原理就是根据不同的视口宽度给网页中 html 根节点设置不同的 font-size。
怎么做:在页面底部引入:

html
<body>
  ......
  <script src="./js/flexible.js"></script>
</body>

易错点:引入位置要按笔记放在 body 末尾;切换模拟器设备后要刷新页面观察根字号变化;如果自己又手动写死 html 字号,可能和 flexible.js 的设置冲突。

rem 移动适配的计算步骤 ​

为什么需要:设计稿给的是 px,页面要写 rem,必须有统一换算方法。
怎么想:先确定基准根字号,再把 px 数值除以基准根字号。
怎么做:

  1. 确定基准根字号:查看设计稿宽度 → 确定参考设备宽度(视口宽度)→ 确定基准根字号(1/10 视口宽度)。
  2. 换算 rem 单位尺寸:rem单位的尺寸 = px单位数值 / 基准根字号。
    例如参考视口宽 375px,基准根字号是 37.5px;设计稿某元素 75px,则 75 / 37.5 = 2rem。
    易错点:除的是“基准根字号”,不是设计稿宽度;设计稿宽度和参考视口宽度要对应;换算后单位写 rem,不要又写回 px。调试时可在谷歌模拟器切换不同设备并刷新,观察 html 字号和元素尺寸是否按比例变化。

课件图示 ​

课件第24页:思考: l 工作中,书写代码的尺寸要参照设计稿尺寸,设计稿中是px还是rem?

课件第25页:l 目标: 计算68px是多少个rem?(设计稿适配375px视口) l N *

课件第21页:思考 l 设备宽度不同,HTML标签字号设置多少合适? p 设备宽度大,元素尺寸

课件第22页:目前rem布局方案中,将网页等分成10份, HTML标签的字号为视口宽度的 1/

代码示例 ​

代码一:移动 Web 基础页面骨架(视口标签 + Flex 宽度适配)

html
<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta http-equiv="X-UA-Compatible" content="IE=edge">

  <!-- 视口标签:width=device-width 视口宽度=设备宽度,initial-scale=1.0 缩放1倍(不缩放) -->
  <meta name="viewport" content="width=device-width, initial-scale=1.0">

  <title>Document</title>
  <style>
    * { margin: 0; padding: 0; }

    /* 宽度适配:Flex 布局,宽度自适应 */
    .nav {
      display: flex;
    }
    .nav div {
      flex: 1;
      height: 44px;
      line-height: 44px;
      text-align: center;
      background-color: pink;
      border-right: 1px solid #fff;
    }
  </style>
</head>
<body>
  <div class="nav">
    <div>首页</div>
    <div>分类</div>
    <div>我的</div>
  </div>
</body>
</html>

要点解释:

  • name="viewport" 中的 viewport 就是视口,作用是约束 HTML 的尺寸;width=device-width 让视口宽度等于设备宽度,initial-scale=1.0 表示不缩放,两者配合让“网页宽度 = 逻辑分辨率宽度”,比例为 1:1。
  • 如果不加视口标签,移动端网页默认宽度是 980,用 375 宽的设备去看,只能把整个页面缩小后塞进去,字小到看不清,所以这一行是移动端页面的前提。
  • 制作网页参考的是逻辑分辨率(不是物理分辨率),记住 iPhone 6/7/8 的逻辑分辨率宽是 375。
  • 易错点:切换谷歌模拟器设备后必须刷新页面才生效;调试栏建议放到左侧或右侧,并且缩放比例保持 100%,否则设备尺寸会被挤小、测出来的数值不准。
  • 易错点:这段只解决了“宽度自适应”,元素高度是写死的 44px,并没有等比缩放,所以后面还要用 rem、vw 来做等比适配。

代码二:媒体查询 + rem 布局(网页等分 10 份)

html
<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>媒体查询 + rem</title>
  <style>
    * { margin: 0; padding: 0; }

    /* 媒体查询:检测视口宽度,条件成立才执行里面的 CSS */
    /* rem 布局方案:网页等分成 10 份,html 字号 = 视口宽度 / 10 */
    @media (width: 320px) {
      html { font-size: 32px; }      /* 320 / 10 */
    }
    @media (width: 375px) {
      html { font-size: 37.5px; }    /* 375 / 10 */
    }
    @media (width: 414px) {
      html { font-size: 41.4px; }    /* 414 / 10 */
    }

    .box {
      /* rem 单位的尺寸 = px单位数值 / 基准根字号 */
      /* 375 下设计稿量得 75px → 75 / 37.5 = 2rem */
      width: 2rem;
      height: 2rem;
      background-color: pink;
    }
    .box2 {
      width: 4rem;    /* 375 下 = 150px,320 下 = 128px,等比缩放 */
      height: 1rem;
      background-color: skyblue;
    }
  </style>
</head>
<body>
  <div class="box"></div>
  <div class="box2"></div>
</body>
</html>

要点解释:

  • rem 是相对单位,1rem = 1 个 HTML 标签的字号大小,所以改变 html 的 font-size 就等于改变整个页面的度量基准。
  • 媒体查询的写法是 @media (width: 320px) { ... },注意是 width 加冒号,不是 max-width,这里匹配的是视口宽度等于该值。
  • 换算公式必须记牢:rem 单位的尺寸 = px 单位数值 ÷ 基准根字号;基准根字号 = 设计稿参考设备宽度 ÷ 10。
  • 易错点:设计稿通常是二倍图(如 iPhone 6/7/8 的设计稿宽度是 750),量出来的 px 要先 ÷2 还原成 375 下的尺寸,再 ÷37.5 换算成 rem;直接拿 750 的数值算,尺寸会大一倍。
  • 易错点:用媒体查询做 rem 适配,只能覆盖到列出来的这几个宽度,其他宽度下 html 没有字号,rem 会失效,所以才有了下面的 flexible.js。

代码三:flexible.js 原理演示(按视口宽度动态设置根字号)

html
<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>flexible.js 适配</title>
  <style>
    * { margin: 0; padding: 0; }

    .box {
      /* 375 设计稿量得 75px,基准根字号 37.5 → 75 / 37.5 = 2rem */
      width: 2rem;
      height: 2rem;
      background-color: pink;
    }
    .box2 {
      width: 4rem;
      height: 1rem;
      background-color: skyblue;
    }
  </style>
</head>
<body>
  <div class="box"></div>
  <div class="box2"></div>

  <!-- 实际项目中直接引入即可,放在 body 最后 -->
  <!-- <script src="./js/flexible.js"></script> -->

  <script>
    // 下面这段为手写简化版,演示 flexible.js 的核心原理:
    // 根据不同的视口宽度给网页中 html 根节点设置不同的 font-size
    (function () {
      function setRemUnit() {
        var html = document.documentElement;
        // 网页等分成 10 份,根字号 = 视口宽度 / 10
        html.style.fontSize = html.clientWidth / 10 + 'px';
      }
      setRemUnit();                              // 页面打开时先执行一次
      window.addEventListener('resize', setRemUnit);  // 视口变化时重新计算
    })();
  </script>
</body>
</html>

要点解释:

  • flexible.js 是手淘开发的移动端适配 js 库,核心原理就是按视口宽度给 html 根节点设置 font-size,从而省掉一大堆媒体查询。
  • 脚本放在 body 最后(或用 src 引入 ./js/flexible.js),保证 DOM 已经解析到,同时尽早设置根字号,避免页面先按默认字号渲染再跳变。
  • 写了 fontSize 之后,CSS 里所有尺寸统一用 rem 写,页面就会在不同设备上等比缩放。
  • 易错点:document.documentElement 拿的才是 html 根节点,写成 document.body 则 rem 不生效。
  • 易错点:换算 rem 之前一定要先确认基准根字号(1/10 视口宽度),并且先确认设计稿是不是二倍图;这两个前提没搞清,后面所有 rem 数值都是错的。

常见错误 ​

1. 切到模拟器后页面“没反应”

  • 现象:点完设备图标,页面还是 PC 版的样子,样式或布局毫无变化。
  • 原因:切换设备只是换了渲染视口,当前文档没有重新加载,浏览器仍沿用旧的布局结果。
  • 修法:切换设备后强制刷新(F5 / ⌘R);同时把缩放比例调回 100%(别用 Ctrl+滚轮缩放页面),调试面板停靠到左右两侧,避免挤压设备宽度。

2. 移动端页面被“缩成一小坨”,字小到看不清

  • 现象:手机上打开自己的页面,整页像缩略图,写多大字号都没用。
  • 原因:<meta name="viewport"> 漏写、写错、被注释掉,或只写了 width 没写 initial-scale,浏览器按默认 980px 宽渲染再整体缩放。
  • 修法:在 <head> 里补齐 width=device-width, initial-scale=1.0;检查引号是英文引号、标签没被 <!-- --> 包住、没被误写进 <style> 里。

3. 照着设计稿量,页面整体“放大一倍”

  • 现象:量出卡片宽 750,写进 CSS 后元素撑满屏幕,跟设计稿完全对不上。
  • 原因:设计稿是二倍图,直接在 1x 像素密度下测量,量到的是物理像素而非逻辑像素。
  • 修法:PSD 用“图像 → 图像大小”改 50%,网页设计稿在渲染面板把像素密度设为 2x,校准后再量;量出的 px 再按 rem 规则换算。

4. rem 换算结果不对,元素大小忽大忽小

  • 现象:同样的 2rem,在不同页面/不同同学手里大小不同,或整体偏大偏小一倍。
  • 原因:基准根字号取错——拿了物理分辨率、拿了二倍图宽度、或团队里有人用 750 有人用 375 作基准。
  • 修法:先固定基准:基准根字号 = 设计稿(逻辑)宽度 ÷ 10;rem = px ÷ 基准根字号;全组统一用同一套基准,别中途换。

5. 媒体查询写了一堆断点,中间尺寸还是错位

  • 现象:320、375、414 都对,一到 360、390 就布局错乱。
  • 原因:用 width: 375px 这种精确匹配写断点,只在该宽度成立,其余宽度全部落空。
  • 修法:要么改用 min-width / max-width 写区间,要么直接交给 flexible.js 动态设 html 字号做连续适配,媒体查询只负责差异化样式(如某些区块隐藏)。

6. flexible.js 引入了却没生效

  • 现象:html 的 font-size 始终是默认值,rem 布局整体崩。
  • 原因:script 路径写错、被放在 <head> 里 DOM 未就绪,或自己在 CSS 里写死了 html { font-size: ... } 把它覆盖了。
  • 修法:<script src="./js/flexible.js"> 放在 </body> 前并核对路径;CSS 里不要再给 html 写固定字号。

7. 宽度自适应了,高度却溢出

  • 现象:百分比 / Flex 布局下横向刚好,纵向内容被挤出去或留大片空白。
  • 原因:只处理了宽度维度,高度仍写死 px,未随视口等比变化。
  • 修法:需要宽高等比缩放的区块改用 rem / vw 描述尺寸;纯横向自适应才用百分比 / Flex,两种方案别在同一维度混用。

练习 ​

练习 1|模拟器基本功 用谷歌模拟器分别切到 iPhone 5、iPhone 6/7/8、iPhone 6/7/8 Plus,打开京东移动站,用元素选择器量同一个按钮的宽度,记录三组数据,并用一句话解释差异来源。要求:切换后刷新、缩放保持 100%、面板停靠右侧。

练习 2|视口对照实验 新建一个含标题和一张图片的页面,做三组对照:① 注释掉 viewport 标签;② 恢复 initial-scale=1.0;③ 改成 initial-scale=2.0。截图记录三种表现,并说明为什么第一种“必然看不清”。

练习 3|二倍图测量与换算 打开课程素材里的游乐园设计稿,先在 2x 像素密度下校准,再量出页面头部高度、某个卡片宽高和一个标题字号,以 375 逻辑宽为基准(基准根字号 37.5)换算成 rem,写出对应 CSS。

练习 4|手写最小适配 不使用 flexible.js,只用媒体查询 + 手算,让 750 设计稿里宽 100px 的元素在 320 / 375 / 414 三种视口下都保持等比(约为视口宽的 13.3%)。写出你的 html 字号设定和元素 CSS。

练习 5|flexible.js 验证 引入 flexible.js 完成游乐园页面头部,在 320 / 375 / 414 / 横屏四种状态下检查元素是否等比缩放;同时回答:如果把 html { font-size: 20px } 写进 CSS,会出现什么后果。

原视频对照 ​

#视频时长
1课程介绍06:10
2谷歌模拟器09:13
3分辨率09:27
4分辨率04:26
5分辨率06:17
6视口08:35
7二倍图08:18

小结 ​

  1. 做移动端只认逻辑分辨率:iPhone 6/7/8 = 375×667 必须张口就来,320 与 414 也要记住。
  2. viewport meta 是移动页面的“入场券”,缺了它一切适配都是白做,默认 980 宽渲染必然错乱。
  3. 模拟器的正确姿势是:切设备 → 刷新 → 缩放 100% → 面板靠边;这三步不做,后面所有调试结论都不可信。
  4. 设计稿一律是二倍图,量之前先切 2x,量完的数值代表逻辑像素,不要直接当 CSS 值用。
  5. rem 只有三步:定基准根字号(视口宽 ÷ 10)→ px ÷ 基准 → 写成 rem;混乱的根源永远是基准没统一。
  6. 分工要清楚:宽度自适应交给百分比 / Flex,等比缩放交给 rem / vw,不要在同一个维度上两套方案混用。
  7. flexible.js 负责动态改 html 字号,媒体查询负责差异化样式;别用 width: 375px 这种精确断点去硬凑设备。
  8. 二倍图解决的是高分屏清晰度问题,不改变元素的逻辑尺寸——元素该多大还是多大,只是素材更清晰。