响应式不是做两套页面

早年做移动端适配,常见做法是单独做一个 m.example.com 手机站,写两套 HTML 两套 CSS,再通过 UA 判断跳转。这种方案的代价是双倍维护:一个 Bug 要改两遍,一次新功能要同步两套代码。响应式设计(Responsive Design)的思路彻底不同——一套 HTML、一套 CSS,通过媒体查询和流式布局让同一个页面在不同宽度下自动调整布局。

它的核心思想可以概括成三句话:流式网格(元素用相对单位而不是固定像素)、弹性图片(图片永远不超过容器宽度)、媒体查询(在不同断点下调整布局)。三者配合,页面就像水一样,倒进什么形状的容器就变成什么形状。

响应式的判断标准:把浏览器窗口从最窄拖到最宽,页面应该全程没有横向滚动条,内容始终可读、布局始终合理。如果中间某个宽度突然崩了,那就是断点没设对。
本文核心知识点
  • 移动优先先写 375px 下的样式,再用 min-width 媒体查询扩展到宽屏。
  • 断点选在布局真的需要变的地方,而不是按设备型号硬写。
  • clamp一行声明让字号、间距在最小值和最大值之间随视口流式缩放。
  • 流式布局用百分比、fr、minmax 代替固定 px,容器变了内容自动跟着变。
响应式设计示意:蓝色渐变背景上从左到右依次排列的桌面显示器、平板与手机三个屏幕框,框间由发光流线连接,体现同一布局跨设备自适应
同一套代码,在不同屏幕尺寸下自动重排——这就是响应式的承诺。

移动优先:先写窄屏再扩展宽屏

响应式最容易搞反的一件事是:先写桌面版,再用媒体查询压成手机版。这种写法会让 CSS 里充满了"本来是三列,窄屏改成一列"的覆盖规则,越写越乱。正确顺序是移动优先——先写手机屏(比如 375px)下的样式,默认就是单列、大按钮、垂直排列;然后用 min-width 媒体查询,在平板和桌面宽度下逐步增强。

/* 第一步:默认样式就是手机版 */
.card-list {
  display: grid;
  grid-template-columns: 1fr;   /* 默认一列 */
  gap: 16px;
  padding: 16px;
}

/* 第二步:平板宽度以上,变两列 */
@media (min-width: 768px) {
  .card-list {
    grid-template-columns: repeat(2, 1fr);
    gap: 24px;
    padding: 24px;
  }
}

/* 第三步:桌面宽度以上,变三列 */
@media (min-width: 1200px) {
  .card-list {
    grid-template-columns: repeat(3, 1fr);
    max-width: 1200px;
    margin: 0 auto;
  }
}

移动优先的好处是:手机用户拿到的就是最精简的 CSS,不需要下载一堆"桌面版专用然后又被覆盖掉"的规则。而且思考顺序也更自然——先想清楚"内容最挤的时候怎么排",再想"宽一点了怎么放开",而不是反过来。

媒体查询与断点怎么选

断点应该选在"布局真的需要变"的宽度,而不是照着某款手机的型号写。常见的做法是先在最窄宽度写好单列布局,然后慢慢拉宽浏览器窗口——当内容看起来挤得不舒服、或者有多余的横向空间被浪费时,那就是加断点的位置。

断点典型设备布局变化
默认(<768px)手机竖屏单列、垂直导航、汉堡菜单
≥768px平板竖屏两列卡片、横向导航开始露出
≥1024px平板横屏 / 小笔记本三列卡片、侧边栏出现
≥1280px桌面显示器限宽居中、更多列展开

几个实用原则:用 min-width 而不是 max-width(配合移动优先);断点不要太多,三四个就够,多了维护不动;不要针对特定设备写断点,比如"iPhone 14 是 390px"——设备每年都在出新的,按内容找断点永远不会过时。

/* 推荐:min-width 移动优先写法 */
@media (min-width: 768px) {
  .card-grid { grid-template-columns: repeat(2, 1fr); }
}
@media (min-width: 1024px) {
  .card-grid { grid-template-columns: repeat(3, 1fr); }
}

/* 不推荐:max-width 桌面优先,覆盖规则会越写越多 */
@media (max-width: 767px) {
  .sidebar { display: none; }
}

流式布局:用百分比和 fr 代替固定像素

响应式的另一半功夫在布局本身。如果一个容器写死了 width: 960px,那在 375px 的手机上必然出现横向滚动条。流式布局的核心是让尺寸用相对单位表达:宽度用百分比、Grid 用 fr、最大宽度用 max-width 限制上限。

/* 一个自适应的内容容器 */
.container {
  width: 100%;
  max-width: 1200px;     /* 桌面下不会太宽 */
  margin: 0 auto;        /* 居中 */
  padding: 0 16px;       /* 两侧留白,手机也不会贴边 */
  box-sizing: border-box; /* padding 算在宽度内,不会溢出 */
}

/* 图片自适应:永远不超过容器 */
img {
  max-width: 100%;
  height: auto;
  display: block;
}

/* Grid 流式卡片墙:一行放几个由容器宽度决定 */
.card-grid {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(260px, 1fr));
  gap: 20px;
}

box-sizing: border-box 是全局必备——不然 padding 和 border 会把元素撑出声明的宽度,导致莫名其妙的横向溢出。几乎所有 CSS 重置样式表第一行都是这个。

clamp 与容器查询:少写媒体查询

字号、间距、圆角这些属性,过去要写一堆媒体查询才能在手机和桌面之间平滑过渡。现在一个 clamp() 就能搞定:它接受三个参数——最小值、理想值、最大值——浏览器自动根据视口宽度在三者之间取一个合适的值。

/* 标题:手机上不小于 24px,桌面不大于 48px,中间随视口缩放 */
.hero-title {
  font-size: clamp(1.5rem, 1rem + 3vw, 3rem);
  line-height: 1.2;
}

/* 段落间距同理 */
.section {
  padding-block: clamp(2rem, 1rem + 5vw, 5rem);
}

容器查询(Container Queries)是更新的能力:以前媒体查询看的是整个视口宽度,现在可以看某个容器自己的宽度。一个卡片组件放在侧栏(窄)和放在主区(宽)里,能自动切换内部布局——这在组件化时代特别有用,因为组件自己不再知道自己会被放在哪里。

.card {
  container-type: inline-size;   /* 声明这是一个查询容器 */
}
@container (min-width: 400px) {
  .card-inner { flex-direction: row; }  /* 容器宽了再横排 */
}