作者:最后一个农民工@掘金,转载需注明出处。
文章中部分示例的源码地址:github.com/fffmoon/res...
前言
从事前端工作这么多年,我有幸亲眼见证和参与了前端页面从"能用"到"精致体验"的演进过程。今天想和大家聊聊响应式设计(Responsive Web Design)从诞生到如今(2025 年)的发展历程,希望能给各位前端伙伴一些启发和思考。同时本文带有自己的理解,某些观点可能是狭隘、片面的,请不吝指正。
1. 远古时代:移动端与桌面端分离
在移动互联网初期,"移动端"和"桌面端"确实是两个完全不同的东西。老前端应该都记得,当时的主流做法是为手机单独制作移动版站点。
例如:百度首页,当我们在 url 加上 m 时候,跳转的是手机版。

实现原理:
javascript
// 典型的UA检测跳转代码
if (
/Android|webOS|iPhone|iPad|iPod|BlackBerry|IEMobile|Opera Mini/i.test(
userAgent
)
) {
deviceType = "移动设备";
isMobile = true;
}
即使现在还有不少项目使用移动桌面分离的方式,因为响应式开发不仅仅是对前端有所要求,对设计师、UED、IXD 也有要求。本人也对两套开发乐此不疲,因为两套意味着更多的收入。
2. 响应式设计的诞生
2010 年,Ethan Marcotte 大佬 在《A List Apart》上发表了一篇开创性文章《Responsive Web Design》,提出了"响应式网页设计"的概念。这真正开启了响应式时代的大门。
先看看效果:

面对设备碎片化日益严重,响应式设计实现了一套代码适配多种设备,并且具有更好的 SEO 表现。 下面看看具体实现:
2.1 流体网格
用百分比(%
) ,而不是像素(px
)来规定每个模块的宽度,让整个骨架结构 能随着屏幕大小平滑地伸缩
css
.container {
width: 90%;
max-width: 1200px;
margin: 0 auto;
}
.column {
width: 48%;
float: left;
margin-right: 4%;
}
.column:last-child {
margin-right: 0;
}
2.2 弹性媒体
让页面里的内容(图片、视频等) 也能自适应容器的宽度,保持布局的完整性和美观。
css
img,
video,
iframe {
max-width: 100%;
height: auto;
}
2.3 媒体查询
特定的屏幕宽度下不同的样式处理
css
/* 基础样式(移动优先) */
.sidebar {
display: none;
}
/* 平板样式 */
@media (min-width: 768px) {
.main-content {
width: 66.66%;
float: left;
}
.sidebar {
display: block;
width: 33.33%;
float: right;
}
}
/* 桌面样式 */
@media (min-width: 1024px) {
.container {
width: 80%;
}
}
3. Bootstrap 与栅格系统时代
众所周知,推广技术的从来不是提案本身,Bootstrap 承担着将响应式设计推广的责任。他的出现极大地降低了响应式设计的实践门槛,让java开发都够快速构建响应式界面。
先看看效果:

再看看具体的实现:
html
<div class="container">
<h1 style="text-align:center;margin:20px 0">Bootstrap栅格系统核心演示</h1>
<div class="info">调整浏览器窗口大小查看响应式变化</div>
<!-- 基本栅格示例 -->
<div class="row">
<div class="col col-sm-6 col-md-4 col-lg-3">
<div class="box">col-sm-6<br />col-md-4<br />col-lg-3</div>
</div>
<div class="col col-sm-6 col-md-4 col-lg-3">
<div class="box">col-sm-6<br />col-md-4<br />col-lg-3</div>
</div>
<div class="col col-sm-6 col-md-4 col-lg-3">
<div class="box">col-sm-6<br />col-md-4<br />col-lg-3</div>
</div>
<div class="col col-sm-6 col-md-4 col-lg-3">
<div class="box">col-sm-6<br />col-md-4<br />col-lg-3</div>
</div>
</div>
<div class="info">
<p>移动优先策略:默认样式针对小屏幕,然后通过媒体查询为更大屏幕增强体验</p>
</div>
</div>
同时期还提出的"移动优先"的设计模式:即先为移动设备设计基础样式,然后通过媒体查询为更大屏幕增强体验,渐进式的网页开发。
4. Flex
flex 布局很早之前叫做 display: box,现代化的 flex 大概在2012年定型,2016 年主流浏览器支持。
即使到现在也可以一套 flex 走天下,flex基本是每个前端的必备技能,就不老生常谈。 这里介绍一个 flex 和流体网格的结合的技巧,在实际业务开发中,经常会遇见搜索表格布局。 具体场景是:搜索在顶部,表格在下面;表格可以滚动,搜索始终保持在顶部。
先看看效果:
表格容器需要使用到 scroll 属性,我遇到很多开发是写一个 js 实现表格高度的计算。但是使用 flex:1;min-h-0; 更高效。
核心代码:
css
.container {
// 注意这里使用了集成父容器的100%高度,如果父容器不存在高度,需要设置 100vh
height: 100%;
width: 100%;
display: flex;
flex-direction: column;
}
.table-container {
flex: 1;
min-height: 0;
overflow: auto;
}
5. CSS Grid 布局
Grid 于 2017 年得到主流浏览器支持,提供了二维布局能力。
Grid 相比 Flex 能用更少的代码实现更加复杂的布局,但遇到复杂的布局后续修改心智负担较重。
css
.container {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(250px, 1fr));
gap: 20px;
}
/* 响应式调整 */
@media (max-width: 768px) {
.container {
grid-template-columns: 1fr;
}
}
6. REM 与动态字体大小方案
淘宝的 lib-flexible 库是这一方案的典型代表,主要解决移动端适配问题。
在实践中还需要配合 cssrem 使用,或者使用vscode的插件px2rem。在vw没有成为标准之前我认为这方案无敌了。
实现原理:
JavaScrip
function setRem() {
const docEl = document.documentElement;
const width = docEl.clientWidth;
const rem = width / 10; // 将屏幕分为10份
docEl.style.fontSize = rem + "px";
}
window.addEventListener("resize", setRem);
setRem();
在CSS中使用rem
CSS
.box {
width: 2rem; /* 相当于屏幕宽度的20% */
height: 1.5rem;
font-size: 0.16rem;
}
7. VW 视口单位方案
vw 单位直接相对于视口宽度,提供了更简洁的响应式方案。
先看看效果:

从预览效果可以看出,字体始终保持和宽度等比例缩放。
核心代码:
css
:root {
--px-to-vw: calc(100vw / 375); /* 基于375px设计稿 */
}
.element {
/* 设计稿中50px的元素 */
width: calc(50 * var(--px-to-vw));
height: calc(100 * var(--px-to-vw));
}
/* 或者使用PostCSS等工具自动转换 */
/* 编译前: */
.box {
width: 50px;
padding: 15px;
}
/* 编译后: */
.box {
width: 13.333vw; /* 50/375*100 */
padding: 4vw; /* 15/375*100 */
}
从代码中可以看出,其实是计算了设计稿的比例,后续所有单位都需要乘以这个比例有点麻烦了,如果是scss类的工具,可以新建函数解决:
scss
/* 移动端页面设计稿宽度 */
$design-width: 750;
@function pxtovw($px) {
@return calc($px / $design-width * 100vw);
}
但是这还是比较麻烦,有没有更加方便的办法?最好是什么额外代码都不加,就能实现!有的,借助 postcss-px-to-viewport 插件将CSS中的px转化为vw。
- 首先我们创建一个vite项目
sh
pnpm create vite@latest px-to-view
选择vue -> 选择JavaScript
- 安装依赖
sh
pnpm i postcss-px-to-viewport -D
- 添加postcss的配置文件 根目录新建 postcss.config.js 添加如下代码,注意要使用ES模块语法
javascirpt
export default {
plugins: {
'postcss-px-to-viewport': {
// 需要转换的单位,默认为 'px'
unitToConvert: 'px',
// 视窗宽度(设计稿宽度),默认 320px
// 建议设置为设计稿的实际宽度(如 750px 对应移动端标准)
viewportWidth: 750,
// 视窗高度(可选,用于计算 vh 单位)
// 如果不需要 vh 转换可不设置
// viewportHeight: 1334,
// 转换后的单位精度(小数点位数)
unitPrecision: 5,
// 需要转换的 CSS 属性列表
// '*' 表示所有属性,'!font*' 表示排除字体相关属性
propList: ['*'],
// 转换后的视窗单位(vw 或 vh)
viewportUnit: 'vw',
// 字体使用的视窗单位
fontViewportUnit: 'vw',
// 不进行转换的选择器黑名单
// 可以是字符串或正则表达式
selectorBlackList: [
// '.ignore', // 忽略所有包含 .ignore 的类
// '.el-button' // 忽略特定组件
],
// 最小的转换像素值(小于此值不转换)
minPixelValue: 1,
// 是否转换媒体查询中的 px 值
mediaQuery: false,
// 是否直接替换值而不是添加备用
replace: true,
// 排除特定文件(正则表达式)
// 如:/node_modules/ 排除 node_modules 中的文件
exclude: /node_modules/,
// 包含特定文件(优先级高于 exclude)
// include: /src/,
// 是否处理横屏模式
landscape: false,
// 横屏模式下的单位
landscapeUnit: 'vw',
// 横屏模式下的视窗宽度
landscapeWidth: 1334,
// 自定义转换函数(高级用法)
// selectorConverter: (selector) => selector,
// 转换规则覆盖(高级用法)
// convertRule: (decl, options) => {}
}
}
}
- 修改HelloWorld.vue组件 添加如下代码
xml
<template>
<div class="test-viewport">我是一个转化过后的元素</div>
</template>
<style lang="css" scoped>
.test-viewport {
width: 750px;
font-size: 40px;
text-align: center;
background: #add8e6;
}
</style>
- 运行项目
markdown
pnpm dev
效果如下:如果页面元素宽度占满则说明配置成功。

8. 混合方案:REM + VW
结合 REM 和 VW 的优点,既能享受 vw 的便捷,又能限制最大最小尺寸。
先看看效果:

核心代码:
css
/* 核心混合方案 */
html {
/* 基于375px设计稿的响应式字体大小 */
font-size: calc(100vw / 375 * 16);
}
/* 限制最大最小尺寸 */
@media (min-width: 768px) {
html {
font-size: calc(768px / 375 * 16);
}
}
@media (max-width: 320px) {
html {
font-size: calc(320px / 375 * 16);
}
}
同样可以借助插件,实现方便的转化。使用 postcss-pxtorem 插件。
- 首先我们创建一个vite项目
sh
pnpm create vite@latest px-to-rem
选择vue >>> 选择JavaScript
- 安装依赖
sh
pnpm i postcss-pxtorem -D
- 添加postcss的配置文件 根目录新建 postcss.config.js 添加如下代码,注意要使用ES模块语法
javascript
export default {
plugins: {
'postcss-pxtorem': {
//根元素字体大小
rootValue: 16,
//匹配CSS中的属性,* 代表启用所有属性
propList: ['*'],
//转换成rem后保留的小数点位数
unitPrecision: 5,
//小于12px的样式不被替换成rem
minPixelValue: 12,
//忽略一些文件,不进行转换,比如我想忽略 依赖的UI框架
exclude: ['node_modules']
}
},
};
- 在styles文件夹下新建vw-rem.css
css
/* vw-rem.css */
:root {
/* 核心公式:基于375px设计稿的响应式字体大小 */
/* 16px * (100vw / 375) = 动态rem基准 */
font-size: calc(100vw / 375 * 16);
}
/* 限制最大最小尺寸 */
@media (min-width: 768px) {
:root {
/* 最大尺寸限制 */
font-size: calc(768px / 375 * 16);
}
}
@media (max-width: 320px) {
:root {
/* 最小尺寸限制 */
font-size: calc(320px / 375 * 16);
}
}
- 在main.js中引入
arduino
import './styles/vw-rem.css'
- 修改 HelloWorld.vue 组件
xml
<script setup>
</script>
<template>
<h1>我是h1标题</h1>
<h2>我是h2标题</h2>
<h3>我是h3标题</h3>
<p>我是p段落</p>
</template>
<style scoped>
h1{
font-size: 48px;
}
</style>
- 预览效果:可以看到在大于768px、小于320px字体是没有变化的。

9. UI组件库的布局组件
在主流UI库中,都有封装好的布局容器,比如:element的Layout响应式布局、Ant Design的Grid栅格、naiveui 的 Grid。这些组件库和Bootstrap大同小异。
我主要讲讲 naiveui 的 Grid 组件。它支持根据自身宽度进行响应式布局,比较有新意。 虽然现在的容器查询能方便快捷的支持这一特性,但在当时容器查询还没成为标准。
先看看效果:

核心代码:
- 使用ResizeObserver API监听容器尺寸变化
javascript
// 使用ResizeObserver监听容器大小变化
const resizeObserver = new ResizeObserver(entries => {
for (let entry of entries) {
if (entry.target === gridContainer) {
updateGridLayout();
}
}
});
resizeObserver.observe(gridContainer);
- 预设多个断点(XS/SM/MD/LG/XL),由于是html实例,这里我将端点值写死,如果在react、vue框架中,可以使用组件化进行封装。
- 动态布局计算,获取当前宽度,根据传入的端点值,设置grid属性
javascript
// 更新网格布局的函数
function updateGridLayout() {
containerWidth = gridContainer.offsetWidth;
// 根据容器宽度设置不同的列数
let cols;
if (containerWidth < 576) {
cols = 1; // XS
} else if (containerWidth < 768) {
cols = 2; // SM
} else if (containerWidth < 992) {
cols = 3; // MD
} else if (containerWidth < 1200) {
cols = 4; // LG
} else {
cols = 6; // XL
}
// 应用列数
responsiveGrid.style.gridTemplateColumns = `repeat(${cols}, 1fr)`;
}
实现完成,如果是在react、vue项目中,可以进行组件封装。
10. 容器查询
随着组件化开发的普及,容器查询是响应式设计的又一次革命,允许组件根据容器尺寸而非视口尺寸进行响应。
先看看效果

核心实现:
css
.card-container {
container-type: inline-size;
container-name: card-container;
}
@container card-container (min-width: 400px) {
.card {
display: flex;
}
.card-image {
width: 30%;
}
.card-content {
width: 70%;
}
}
@container card-container (max-width: 399px) {
.card-image {
width: 100%;
}
}
容器查询和组件化类似,可以将代码样式仅依赖于其容器尺寸,而非全局视口尺寸。做到和组件一样复用。
11. UnoCSS 与原子化响应式
原子化 CSS 框架如 UnoCSS、TailwindCSS 提供了新的响应式思路。
首先需要在 uno.config.ts 中设置响应式变量,如果不设置就是默认。但实际开发中,断点变量不仅是在unocss使用,通常还会在js、hook中使用。
php
import { defineConfig, presetAttributify, presetUno, transformerDirectives } from 'unocss'
// ... 其他代码
presets: [
presetUno({
theme: {
breakpoints: {
xs: '575.98px',
sm: '767.98px',
md: '991.98px',
lg: '1199.98px',
xl: '1399.98px',
},
},
}),
],
这里我自定义了 UnoCSS 的响应式断点,其值借鉴了 Bootstrap 框架的默认断点设置
xml
<div class="w-full md:w-1/2 lg:w-1/3 xl:w-1/4 p-4 md:p-6 lg:p-8">
<!-- 内容 -->
</div>
<!-- UnoCSS中的响应式 -->
<div class="text-14px sm:text-16px md:text-18px">响应式文字大小</div>
总结当前响应式方案选择
项目类型 | 推荐方案 | 优势说明 |
---|---|---|
移动端H5 | REM + VW混合方案+PostCSS | 兼顾灵活性和可控性 |
数据可视化大屏 | VW/VH视口单位 +PostCSS | 完美适配各种分辨率 |
管理后台、复杂应用 | Flex + Grid + 容器查询 | 复杂布局处理能力强 |
内容型网站 | CSS Grid + 媒体查询 | SEO友好,语义化强 |
未来趋势
纵观响应式设计的发展历程,都是围绕html、css的能力的不断增强,从最开始的Bootstrap到现在的原子css,html和css结合似乎是未来趋势。我敢断定,Bootstrap 的思想还将继续影响前端生态系统。
参考资料:
- alistapart.com/article/res... - 响应式设计的开创性文章
- developer.mozilla.org/en-US/docs/... - 最权威的 CSS Grid 参考资料
- developer.mozilla.org/en-US/docs/... - 完整的 Flexbox 指南
- www.w3.org/TR/css-cont... - W3C 官方容器查询规范
- getbootstrap.com/docs/5.3/la... - 最流行的栅格系统实现
- github.com/amfe/articl... - 淘宝移动端适配方案分析
- web.dev/articles/re... - Google Web Dev 的响应式设计指南
- developer.mozilla.org/en-US/docs/... - MDN 容器查询实践指南