做网页开发绕不开一个问题:同一份HTML代码,怎么让它在手机、平板、电脑上都有不错的展示效果?答案就是CSS媒体查询。它相当于给样式表加了一层条件判断,浏览器会根据当前设备的具体情况决定哪些样式生效、哪些不生效。理解了媒体查询,才算真正理解响应式设计。

媒体查询的基本语法与工作原理
媒体查询由两部分组成:一个可选的媒体类型和一个或多个媒体特性表达式。媒体类型常见的是screen(屏幕)和print(打印),媒体特性则用来描述设备的具体条件,比如视口宽度、高度、屏幕方向、分辨率等。只有当整个表达式结果为真时,花括号内的样式才会被应用。
媒体查询有两种常见的写法。第一种是写在样式表内部,用@media规则包裹:
/* 视口宽度小于等于 768px 时生效 */
@media screen and (max-width: 768px) {
.container {
flex-direction: column;
}
}
/* 视口宽度大于等于 769px 时生效 */
@media screen and (min-width: 769px) {
.container {
flex-direction: row;
}
}第二种是写在<link>标签上,通过media属性指定条件,条件不满足时浏览器甚至不会下载该样式文件(部分浏览器行为),这对性能优化有一定帮助:
<link rel="stylesheet" href="mobile.css" media="screen and (max-width: 768px)"> <link rel="stylesheet" href="desktop.css" media="screen and (min-width: 769px)">
需要注意的是,媒体查询判断的是视口宽度,不是浏览器窗口的物理宽度,更不是设备屏幕的宽度。这也解释了为什么移动端页面必须在HTML头部加上viewport设置,否则手机浏览器会以默认980px宽的虚拟视口渲染页面,媒体查询的结果就全乱了。
min-width与max-width的区别及断点设计
刚接触媒体查询的人最容易混淆的就是min-width和max-width。它们的判断逻辑完全相反:min-width: 768px表示视口宽度大于等于768px时生效,是向上覆盖;max-width: 768px表示视口宽度小于等于768px时生效,是向下覆盖。一个字符的差别,语义完全不同,写错了页面断点切换就会颠倒。
这两种写法对应了两种开发思路。用max-width的是桌面优先:先写桌面端样式,再用媒体查询逐步适配小屏幕。用min-width的是移动优先:先写手机端的基础样式,屏幕变大后再增强。目前业界普遍推荐移动优先,原因很简单,移动端设备性能相对较弱,让它加载最基础的样式,复杂样式只在桌面端生效,整体性能分布更合理。
移动优先的典型写法如下:
/* 基础样式:手机端 */
.card {
width: 100%;
padding: 12px;
}
/* 平板:大于等于 768px */
@media (min-width: 768px) {
.card {
width: 50%;
padding: 20px;
}
}
/* 桌面:大于等于 1200px */
@media (min-width: 1200px) {
.card {
width: 33.33%;
padding: 24px;
}
}关于断点的选择,不建议照搬某款具体设备的宽度,比如把iPhone某一代的像素宽写死成断点。更合理的做法是根据内容和布局来确定断点:不断缩小浏览器窗口,当布局开始显得拥挤难看时,那个位置就是该设置断点的地方。当然实践中也需要一些约定俗成的参考值,常见的有576px、768px、992px、1200px这几个档位,覆盖了从小屏手机到宽屏桌面的主流场景。
常用媒体特性与组合条件
除了宽度,媒体查询还支持很多实用的特性。orientation可以判断横屏还是竖屏,对视频类、游戏类页面很有用:@media (orientation: landscape)在横屏时生效。hover可以判断设备是否支持悬停,用它区分鼠标用户和触屏用户比判断屏幕宽度更准确:@media (hover: hover)表示主输入设备支持悬停。
多个条件可以用and、or、not组合。要注意的是,在标准语法中or是通过逗号表达的,而不是直接写or这个单词:
/* 宽度在 600px 到 900px 之间 */
@media (min-width: 600px) and (max-width: 900px) {
.banner { font-size: 20px; }
}
/* 竖屏 或者 宽度小于 480px,任一条件满足即生效 */
@media (orientation: portrait), (max-width: 480px) {
.sidebar { display: none; }
}
/* 支持悬停且指针精确的设备 */
@media (hover: hover) and (pointer: fine) {
.btn:hover { background: #0056b3; }
}还有一个容易被忽略的场景是打印样式。给页面加一份@media print的规则,可以隐藏导航栏、去掉背景色、调整字号,用户打印或导出PDF时体验会好很多:
@media print {
nav, .sidebar, .footer-btn {
display: none;
}
body {
font-size: 12pt;
color: #000;
background: #fff;
}
a::after {
content: " (" attr(href) ")";
}
}常见问题与注意事项
第一个高频问题是忘记写viewport设置。如果HTML头部没有<meta name="viewport" content="width=device-width, initial-scale=1">这行,手机浏览器会按桌面宽度渲染再整体缩放,媒体查询判断的宽度与实际视觉完全对不上,断点看起来全部失灵。排查响应式问题时第一步就应该检查它。
第二个是样式的层叠覆盖问题。媒体查询并不改变CSS的优先级规则,它内部的样式依然遵循后来居上、特异性高者胜的原则。如果移动优先写法中,某个规则的选择器特异性更高,后面的媒体查询可能压不过它。一个实用的经验是:媒体查询内部的选择器尽量与基础样式的选择器保持一致,让代码顺序决定覆盖关系,出问题时容易排查。
第三个是边界值的衔接。如果一处用max-width: 768px,另一处用min-width: 768px,那么在宽度恰好等于768px时两条规则会同时命中,样式相互打架。规范的做法是错开一个像素,比如一个写768px,另一个写769px,保证任何宽度下只有一套规则生效。
最后提一点,媒体查询判断的是视口而非单个元素,所以它无法实现“某个区块宽度小于多少时改变样式”这种需求。这类需求可以配合容器查询(Container Query)来解决,@container语法如今在主流浏览器中已可用,原理与媒体查询类似,只是判断对象换成了容器元素,两者结合使用能让组件级响应式做得更加灵活。
总体来说,媒体查询上手不难,难的是断点规划合理、样式组织清晰。建议新项目一开始就确定移动优先的书写习惯,把断点数量控制在合理范围,并结合浏览器开发者工具的设备模拟功能反复验证,响应式页面的质量自然会有保障。