Skip to content

响应式与移动端适配

一、响应式设计基础

1.1 视口设置

html
<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no">
属性说明
width=device-width视口宽度等于设备宽度
initial-scale=1.0初始缩放比例
maximum-scale=1.0最大缩放比例
user-scalable=no禁止用户缩放

1.2 媒体查询

css
/* 基础语法 */
@media screen and (max-width: 768px) {
  .container { padding: 10px; }
}

/* 常用断点 */
@media (max-width: 576px) { /* 手机 */ }
@media (min-width: 577px) and (max-width: 768px) { /* 平板竖屏 */ }
@media (min-width: 769px) and (max-width: 992px) { /* 平板横屏 */ }
@media (min-width: 993px) and (max-width: 1200px) { /* 桌面 */ }
@media (min-width: 1201px) { /* 大屏 */ }

/* 其他查询条件 */
@media (orientation: portrait) { /* 竖屏 */ }
@media (orientation: landscape) { /* 横屏 */ }
@media (hover: hover) { /* 支持 hover */ }
@media (prefers-color-scheme: dark) { /* 深色模式 */ }

1.3 响应式单位

单位说明
%相对父元素
vw/vh视口宽度/高度的 1%
vmin/vmaxvw 和 vh 中的较小/较大值
rem相对根元素 font-size
em相对当前元素 font-size

二、移动端适配方案

2.1 rem 方案

javascript
// 动态设置根元素 font-size
function setRem() {
  const designWidth = 750;  // 设计稿宽度
  const clientWidth = document.documentElement.clientWidth;
  const rem = (clientWidth / designWidth) * 100;
  document.documentElement.style.fontSize = rem + 'px';
}

window.addEventListener('resize', setRem);
setRem();
css
/* 设计稿上 100px = 1rem */
.box {
  width: 3.5rem;   /* 设计稿 350px */
  height: 1.2rem;  /* 设计稿 120px */
}

工具推荐: postcss-pxtorem 自动转换

2.2 vw 方案 (推荐)

css
/* 设计稿 750px 宽度,100vw = 750px */
/* 1px = 100/750 vw ≈ 0.1333vw */

.box {
  width: 46.67vw;   /* 设计稿 350px */
  height: 16vw;     /* 设计稿 120px */
}

工具推荐: postcss-px-to-viewport 自动转换

javascript
// postcss.config.js
module.exports = {
  plugins: {
    'postcss-px-to-viewport': {
      viewportWidth: 750,
      unitPrecision: 5,
      viewportUnit: 'vw',
      minPixelValue: 1
    }
  }
}

2.3 rem + vw 混合方案

css
/* 根元素使用 vw */
html {
  font-size: calc(100vw / 7.5);  /* 750 设计稿: 1rem = 100px */
}

/* 最大最小限制 */
@media (min-width: 750px) {
  html { font-size: 100px; }
}
@media (max-width: 320px) {
  html { font-size: 42.67px; }
}

三、1px 问题

3.1 问题原因

在高 DPR (Device Pixel Ratio) 设备上,CSS 的 1px 会被渲染成多个物理像素,导致边框看起来偏粗。

3.2 解决方案

方案一:transform 缩放 (推荐)

css
.border-1px {
  position: relative;
}

.border-1px::after {
  content: '';
  position: absolute;
  left: 0;
  bottom: 0;
  width: 100%;
  height: 1px;
  background: #000;
  transform: scaleY(0.5);
  transform-origin: 0 0;
}

/* 四边框 */
.border-1px-all::after {
  content: '';
  position: absolute;
  top: 0;
  left: 0;
  width: 200%;
  height: 200%;
  border: 1px solid #000;
  transform: scale(0.5);
  transform-origin: 0 0;
  box-sizing: border-box;
  border-radius: 8px; /* 需要时加圆角 */
}

方案二:媒体查询 + DPR

css
.border {
  border: 1px solid #000;
}

@media (-webkit-min-device-pixel-ratio: 2) {
  .border {
    border-width: 0.5px;
  }
}

@media (-webkit-min-device-pixel-ratio: 3) {
  .border {
    border-width: 0.333px;
  }
}

方案三:viewport 缩放

javascript
const dpr = window.devicePixelRatio || 1;
const scale = 1 / dpr;
const viewport = document.querySelector('meta[name="viewport"]');
viewport.content = `width=device-width,initial-scale=${scale},maximum-scale=${scale}`;

// 同时调整 rem 基准值
document.documentElement.style.fontSize = dpr * 100 + 'px';

四、安全区域适配

4.1 iPhone 刘海屏适配

html
<meta name="viewport" content="width=device-width, initial-scale=1.0, viewport-fit=cover">
css
/* 使用 safe-area-inset-* 环境变量 */
.fixed-bottom {
  padding-bottom: constant(safe-area-inset-bottom); /* iOS < 11.2 */
  padding-bottom: env(safe-area-inset-bottom);      /* iOS >= 11.2 */
}

/* 结合 calc */
.tabbar {
  height: calc(50px + env(safe-area-inset-bottom));
  padding-bottom: env(safe-area-inset-bottom);
}

4.2 环境变量

css
env(safe-area-inset-top)     /* 顶部安全距离 */
env(safe-area-inset-right)   /* 右侧安全距离 */
env(safe-area-inset-bottom)  /* 底部安全距离 (Home 指示条) */
env(safe-area-inset-left)    /* 左侧安全距离 */

五、图片适配

5.1 响应式图片

html
<!-- srcset + sizes -->
<img
  src="small.jpg"
  srcset="small.jpg 400w, medium.jpg 800w, large.jpg 1200w"
  sizes="(max-width: 600px) 100vw, 50vw"
  alt="响应式图片"
>

<!-- picture 元素 -->
<picture>
  <source media="(max-width: 600px)" srcset="mobile.jpg">
  <source media="(max-width: 1200px)" srcset="tablet.jpg">
  <img src="desktop.jpg" alt="响应式图片">
</picture>

5.2 高清屏图片

css
/* 使用 2x/3x 图片 */
.logo {
  background-image: url('logo.png');
}

@media (-webkit-min-device-pixel-ratio: 2) {
  .logo {
    background-image: url('logo@2x.png');
  }
}

/* 或使用 image-set */
.logo {
  background-image: image-set(
    url('logo.png') 1x,
    url('logo@2x.png') 2x,
    url('logo@3x.png') 3x
  );
}

六、高频面试题

Q1: rem 和 vw 方案的优缺点?

方案优点缺点
rem兼容性好需要 JS 动态计算
vw纯 CSS,无需 JS旧浏览器不支持

Q2: 如何解决 1px 问题?

答案: 使用伪元素 + transform: scaleY(0.5) 是最通用的方案。

Q3: 什么是安全区域?如何适配?

答案: 安全区域是 iPhone X 等刘海屏设备的可视区域。使用 env(safe-area-inset-*) 环境变量来获取安全距离,并在固定定位元素上添加相应的 padding。

前端面试知识库