目录
响应式布局
引子
理解响应式网页和响应式布局原理
媒体查询
什么是媒体查询?
什么是媒体类型?
什么是媒体特性?
媒体查询语法
仿三星官网首页
响应式布局
引子
通过前面两节的学习
CSS - 移动端布局(一)关键的前置知识_伏城之外的博客-CSDN博客
CSS - 移动端布局(二)移动端适配_伏城之外的博客-CSDN博客
我们已经了解如何开发能够适配移动端屏幕的网页,普适策略是使用meta viewport + rem布局 或 vw布局,但是这样开发出来的网页也仅仅只能适配移动端屏幕
而无法适配PC端屏幕,当我们在PC端屏幕查看移动WEB网页时,实际效果如下:
导致移动网页在PC端显示效果不佳的原因是因为:
- 移动WEB网页的宽度一般取移动浏览器视觉视口的宽度,当移动WEB网页在PC浏览器显示时,则其宽度也是取PC浏览器视觉视口的宽度,因此移动WEB网页在PC浏览器显示时会占满全屏
- 移动WEB网页的布局尺寸一般都是动态尺寸,比如rem尺寸,vw尺寸,这些尺寸都与浏览器的视觉视口的宽度正相关,因此当浏览器从移动端的窄视口变为PC端的宽视口时,移动WEB网页的布局尺寸将同比放大,用户最直观的感受就是在PC浏览器上,移动WEB网页被放大了
因此,网站一般会避免用户在PC端访问移动WEB网页,同理,网站也会避免将PC网页放在移动端设备上展示,处理策略是:
- 设计两套网页,一套PC WEB网页,一套移动WEB网页
- 当用户访问网站网页时,网站服务器会判断用户发送的请求是来自PC浏览器,还是移动浏览器(根据HTTP请求头中的User-Agent),如果是移动浏览器,则响应移动WEB网页,如果是PC浏览器,则响应PC WEB网页
当请求来自PC浏览器时,请求头中User-Agent信息如下
当请求来自移动浏览器时,请求头中User-Agent信息如下
但是这种处理策略,需要网站维护两套网页,甚至维护两套网站,在设计、开发和运维成本上都是double的,这是缺点。当然烧钱的东西,肯定是有优点的,如果维护两套网站的话,即一套服务于PC端用户的网站,一套服务于移动端用户的网站,公司就可以根据用户流量来合理安排服务器资源,避免资源浪费或资源竞争,另外对于前端开发来说不需要在一个网页中同时实现适配PC浏览器和移动浏览器,开发难度也会大大降低。
当然,上面这种策略是大公司、大网站才需要玩的,对于小公司,小网站来说,这种烧钱的策略是没必要的。因此,一套网页同时支持适配PC和移动端的技术是有必要研究的,而我们将同时能够适配PC端和移动端的网页称为响应式网页。
理解响应式网页和响应式布局原理
三星电子 中国 | 三星手机 | 电视 | 显示器 固态硬盘 | 冰箱 洗衣机等产品官网 (samsung.com)
三星的官网首页就是一个响应式网页,即该网页既支持适配PC端,又支持适配移动端。
如下是该网页在PC端1920x1080分辨率下的显示效果
如下是该网页在平板768x1024分辨率下的显示效果
如下是该网页在手机375x812分辨率下的显示效果
我们可以发现,响应式网页无论在PC端,还是在平板、手机这些移动端设备上都能够实现良好适配,并且这种适配并不需要改变网页的内容结构(HTML),只需要改变网页内容的布局(CSS),比如上面三个截图中网页的内容并未发生改变,只是网页内容的布局发生了改变。
而我们将可以随着浏览器视觉视口分辨率的改变而改变的网页布局,称为响应式布局。
因此响应式布局的原理很简单,那就是监听浏览器视觉视口宽度的变化,当浏览器视觉视口宽度变化到一定程度或一定范围内时,网页的布局就改变,即网页采用另一套样式。
而监听浏览器视觉视口宽度的变化,我们最先想到的就是通过JS的事件,如window.onload和window.onresize,下面我们来通过JS实现一个简单的响应式布局
JS事件监听触发响应式布局-Javascript文档类资源-CSDN文库
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><style>* {margin: 0;padding: 0;box-sizing: border-box;}</style><link rel="stylesheet" id="responsive" /><script>const selectStyle = () => {const viewportWidth = document.documentElement.clientWidth;const stylesheet = document.querySelector("#responsive");if (viewportWidth >= 1440) {stylesheet.href = "./min1440.css";} else if (viewportWidth >= 768) {stylesheet.href = "./min768.css";} else {stylesheet.href = "./max768.css";}};window.onload = selectStyle;window.onresize = selectStyle;</script></head><body><br /><div class="w"><div class="footer-nav"><input type="checkbox" id="product-service" style="display: none" /><dl><dt><label for="product-service">产品与服务</label></dt><dd>智能手机</dd><dd>智能平板</dd><dd>音频设配</dd><dd>智能手表</dd><dd>S 换机助手</dd><dd>智能产品配件</dd><dd>电视</dd><dd>冰箱</dd><dd>洗衣机</dd><dd>空调</dd><dd>显示器</dd><dd>存储产品</dd></dl><input type="checkbox" id="purchase-channels" style="display: none" /><dl><dt><label for="purchase-channels">购买渠道</label></dt><dd>最新活动</dd><dd>网上商城</dd><dd>三星Galaxy授权体验店</dd><dd>电视/影音产品门店查询</dd><dd>冰箱/洗衣机产品门店查询</dd><dd>非全新机</dd><dd>官方翻新机</dd></dl><input type="checkbox" id="service-support" style="display: none" /><dl><dt><label for="service-support">服务支持</label></dt><dd>在线服务</dd><dd>邮件咨询</dd><dd>联系我们</dd><dd>服务中心查询</dd><dd>体验反馈</dd><dd>ECO信息</dd><dd>三星中国开发者</dd><dd>专属管家服务</dd><dd>尊贵管家服务</dd></dl><input type="checkbox" id="about-us" style="display: none" /><dl><dt><label for="about-us">关于我们</label></dt><dd>公司介绍</dd><dd>业务范围</dd><dd>品牌标识</dd><dd>人才招聘</dd><dd>投资者关系</dd><dd>新闻中心</dd><dd>伦理道德</dd></dl></div></div></body>
</html>
min1440.css
/* 当浏览器视觉视口宽度 >= 1440 时,采用如下样式 */.w {width: 1440px;margin: 0 auto;
}.footer-nav {width: 100%;border-top: 1px solid #ddd;border-bottom: 1px solid #ddd;display: grid;grid-template-columns: repeat(4, 1fr);grid-template-rows: 1fr;
}.footer-nav > dl:not(:last-child) {padding-bottom: 32px;border-right: 1px solid #ddd;
}.footer-nav > dl > dt {padding: 16px 24px;font-size: 18px;font-weight: bold;
}.footer-nav > dl > dd {padding: 7px 24px;font-size: 14px;cursor: pointer;
}.footer-nav > dl > dd:hover {text-decoration: underline;color: #1428a0;
}
min768.css
/* 当浏览器视觉视口宽度在 [768, 1440) 时,采用如下样式 */.w {width: 100%;
}.footer-nav {width: 100%;border-top: 1px solid #ddd;border-bottom: 1px solid #ddd;display: grid;grid-template-columns: repeat(4, 1fr);grid-template-rows: 1fr;
}.footer-nav>dl:not(:last-child) {padding-bottom: calc(100vw / 1440 * 32);border-right: 1px solid #ddd;
}.footer-nav>dl>dt {padding: calc(100vw / 1440 * 16) calc(100vw / 1440 * 24);font-size: calc(100vw / 1440 * 18);font-weight: bold;
}.footer-nav>dl>dd {padding: calc(100vw / 1440 * 7) calc(100vw / 1440 * 24);font-size: calc(100vw / 1440 * 14);cursor: pointer;
}.footer-nav>dl>dd:hover {text-decoration: underline;color: #1428a0;
}
max768.css
/* 当浏览器视觉视口宽度 < 768 时,采用如下样式 */.w {width: 100%;
}.footer-nav {width: 100%;border-top: 1px solid #ddd;
}.footer-nav > dl > dt {margin: 0 calc(100vw / 767 * 25);padding: calc(100vw / 767 * 36) calc(100vw / 767 * 25);border-bottom: 1px solid #ddd;font-size: calc(100vw / 767 * 34);font-weight: 700;
}.footer-nav > dl > dt > label {cursor: pointer;user-select: none;
}.footer-nav > dl > dd {display: none;padding: calc(100vw / 767 * 21) calc(100vw / 767 * 59.6) calc(100vw / 767 * 21) calc(100vw / 767 * 68);font-size: calc(100vw / 767 * 29.8);cursor: pointer;
}.footer-nav > dl > dd:hover {text-decoration: underline;color: #1428a0;
}#product-service:checked + dl > dd,
#purchase-channels:checked + dl > dd,
#service-support:checked + dl > dd,
#about-us:checked + dl > dd {display: block;
}#product-service:checked + dl > dt,
#purchase-channels:checked + dl > dt,
#service-support:checked + dl > dt,
#about-us:checked + dl > dt {border-bottom: none;
}
实现效果如下:
当浏览器视觉视口宽度 >= 1440 时
当浏览器视觉视口宽度在 [768, 1440) 时
当浏览器视觉视口宽度 < 768 时
通过上面的实操,我们可以总结出响应式布局的实现原理,其实就是为一个网页结构设计多套样式,这里的多套样式需要能够使网页适配不同分辨率的屏幕。
但是,上面通过JS监听浏览器视觉视口宽度变化的方式促发响应式布局,非常不优雅,而当我们使浏览器视觉视口resize时,网页布局会出现抖动,这里的抖动其实是:JS监听到了视觉视口的宽度变化,然后触发onresize事件回调,卸载当前样式文件,然后加载新的样式文件。
而闪动的原因就是JS卸载样式文件时,网页会处于无样式的裸奔状态,虽然时间很短,但是也会出现,而后加载完新的样式文件后,网页有了样式,当视觉视口不断地resize时,网页就会不断卸载样式、加载样式,此时我们就可以看到明显的布局抖动。
因此,通过JS来触发响应式布局,并不是一种好的方式。
为了实现平滑、顺畅、无抖动的响应式布局,CSS3推出了媒体查询。
媒体查询
什么是媒体查询?
媒体查询,即media query,它是一种CSS语法,支持:
- 针对不同的媒体类型(media type)定义不同的样式
- 针对某媒体类型下不同的媒体特性(media feature)定义不同的样式
什么是媒体类型?
早期,网页不仅可以在电脑浏览器上显示,还可以在打印机中打印,还可以在投影仪上投影,以及在电视上显示等等,而这里的电脑、打印机、投影仪、电视就是媒体,后期,网页越来越多地在手机、平板等移动端设备上显示,而这里地手机、平板也是媒体,为了更好的区分这些媒体,CSS将它们划分为了以下几个媒体类型:
- screen :屏幕媒体,包括电脑、手机、平板
- print:打印媒体,如打印机
projection:投影媒体,如投影仪tv:电视媒体,如电视- all:所有媒体
但是,上述几个媒体类型中投影媒体和电视媒体几乎已经不用了,因此主要的媒体类型就三个 screen、print、all,其中screen是最最常用的媒体类型。
什么是媒体特性?
我们大多只关注screen媒体类型的媒体特性,screen媒体类型的常用特性有如下三个:
min-width | 视觉视口的最小宽度 |
max-width | 视觉视口的最大宽度 |
orientation | 屏幕处于横屏(landscape)还是竖屏(portrait) |
媒体查询语法
媒体查询可以定义在三个地方:
- link标签属性中
- @import url语法中
- @media语法中
link标签属性中定义媒体查询的语法如下:
<link rel="stylesheet" href="./min1440.css" media="screen and (min-width: 1440px)">
<link rel="stylesheet" href="./min768.css" media="screen and (min-width: 768px) and (max-width: 1439px)">
<link rel="stylesheet" href="./max768.css" media="screen and (max-width: 767px)">
将媒体查询定义在link标签的media属性中,如上三个link标签的含义分别为:
- 当媒体类型为screen时(即电脑、手机、平板),且(and语义) 浏览器视觉视口宽度最小不低于1440px时(即≥1440px),应用./min1440.css到网页中
- 当媒体类型为screen时,且浏览器视觉视口宽度范围在 [768px, 1439px] 时,应用./min768.css到网页中
- 当媒体类型为screen时,且浏览器视觉视口宽度最大不超过 767px时(即≤767px),应用./max768.css到网页中
link标签定义媒体查询不是一种推荐方式,当存在大量媒体查询时,会产生大量link标签,这样不仅不美观,还会阻塞JS的加载执行,进而影响整体网页的渲染速度。
@import url语法定义媒体查询的语法如下:
@import url可以定义在HTML的style标签中,也可以定义在css文件中
<style>@import url(./min1440.css) screen and (min-width: 1440px);@import url(./min768.css) screen and (min-width: 768px) and (max-width: 1439px);@import url(./max768.css) screen and (max-width: 767px);
</style>
上面三句@import中添加的媒体查询含义和前面一致
由于@import会等到所有JS加载执行完毕后,才会加载css文件,因此不会阻塞网页的渲染,因此我们推荐在@import语法中定义媒体查询。但是@import只能针对样式文件进行操作,不能针对单个样式或局部样式进行操作,因此我们还需要借助第三种媒体查询定义方式@media。
@media定义媒体查询的语法如下:
<style>@media screen and (min-width: 1440px) {.w {width: 1440px;margin: 0 auto;}.footer-nav {width: 100%;border-top: 1px solid #ddd;border-bottom: 1px solid #ddd;display: grid;grid-template-columns: repeat(4, 1fr);grid-template-rows: 1fr;}.footer-nav > dl:not(:last-child) {padding-bottom: 32px;border-right: 1px solid #ddd;}.footer-nav > dl > dt {padding: 16px 24px;font-size: 18px;font-weight: bold;}.footer-nav > dl > dd {padding: 7px 24px;font-size: 14px;cursor: pointer;}.footer-nav > dl > dd:hover {text-decoration: underline;color: #1428a0;}}@media screen and (min-width: 768px) and (max-width: 1439px) {.w {width: 100%;}.footer-nav {width: 100%;border-top: 1px solid #ddd;border-bottom: 1px solid #ddd;display: grid;grid-template-columns: repeat(4, 1fr);grid-template-rows: 1fr;}.footer-nav > dl:not(:last-child) {padding-bottom: calc(100vw / 1440 * 32);border-right: 1px solid #ddd;}.footer-nav > dl > dt {padding: calc(100vw / 1440 * 16) calc(100vw / 1440 * 24);font-size: calc(100vw / 1440 * 18);font-weight: bold;}.footer-nav > dl > dd {padding: calc(100vw / 1440 * 7) calc(100vw / 1440 * 24);font-size: calc(100vw / 1440 * 14);cursor: pointer;}.footer-nav > dl > dd:hover {text-decoration: underline;color: #1428a0;}}@media screen and (max-width: 767px) {.w {width: 100%;}.footer-nav {width: 100%;border-top: 1px solid #ddd;}.footer-nav > dl > dt {margin: 0 calc(100vw / 767 * 25);padding: calc(100vw / 767 * 36) calc(100vw / 767 * 25);border-bottom: 1px solid #ddd;font-size: calc(100vw / 767 * 34);font-weight: 700;}.footer-nav > dl > dt > label {cursor: pointer;user-select: none;}.footer-nav > dl > dd {display: none;padding: calc(100vw / 767 * 21) calc(100vw / 767 * 59.6)calc(100vw / 767 * 21) calc(100vw / 767 * 68);font-size: calc(100vw / 767 * 29.8);cursor: pointer;}.footer-nav > dl > dd:hover {text-decoration: underline;color: #1428a0;}#product-service:checked + dl > dd,#purchase-channels:checked + dl > dd,#service-support:checked + dl > dd,#about-us:checked + dl > dd {display: block;}#product-service:checked + dl > dt,#purchase-channels:checked + dl > dt,#service-support:checked + dl > dt,#about-us:checked + dl > dt {border-bottom: none;}}
</style>
在@media中,我们可以为某些媒体类型、媒体特性定义单个或多个样式,灵活性更高。
无论使用哪种方式定义媒体查询,我们需要注意如下几点:
- 媒体查询中,媒体类型是必要条件,必须定义
- 媒体查询中,媒体特性是次要条件,可以不定义,如果定义的话,则必须用小括号括起来,且一个小括号中只能定义一个媒体特性
- 媒体查询中,媒体类型和媒体类型之间,媒体类型和媒体特性之间、媒体特性与媒体特性之间需要使用逻辑操作符用关联,逻辑操作符即常用的“与”、“或”、“非”、“仅”
媒体查询中的逻辑操作符介绍:
- “与”:and
- "或":,
- “非”:not
- "仅":only
我们通过几个实例来理解媒体查询中的逻辑操作符:
@import url(./min768.css) screen and (min-width: 768px) and (max-width: 1439px);
上面媒体查询含义是:当 “媒体类型为screen 且 视觉视口宽度≥768px 且 视觉视口宽度≤1439px” 时,才应用min768.css到网页。
@import url(./min768.css) screen,print;
上面媒体查询含义是:当 “媒体类型为screen 或 print ,才应用min768.css到网页。
@import url(./min768.css) screen and (max-width: 768px), screen and (min-width: 1439px);
上面媒体查询含义是:当 “媒体类型为screen 且 视觉视口宽度≤768px“ 或者 “媒体类型为screen 且 视觉视口宽度≥1439px“
我们需要注意的是,”或“逻辑操作符,如果在媒体特性后面加逗号,表示媒体查询结束,逗号后面将定义一个新的媒体查询。
@import url(./min768.css) not screen;
上面媒体查询含义是:如果媒体类型为screen,则不应用样式。
@import url(./min768.css) not screen and (min-width: 768px) and (max-width: 1439px);
上面媒体查询含义是:如果媒体类型为screen,且视觉视口宽度≥768px, 且视觉视口宽度≤1439px 时,则不应用样式,其余情况则应用样式。
我们需要注意的是not目前只能用于否定整个媒体查询的条件,不能用于否定单个媒体功能表达式,因此not一般写在媒体类型前面。
@import url(./min768.css) only screen and (min-width: 768px) and (max-width: 1439px);
上面媒体查询含义是:仅在“媒体类型为screen 且 视觉视口宽度≥768px 且 视觉视口宽度≤1439px”时,应用min768.css样式文件到网页。
我们需要注意的是,only只能用于限制整个媒体查询,不能用于限制单个媒体功能表达式,因此only的逻辑操作功能很鸡肋,但是only还有一个妙用,那就是当浏览器不支持媒体功能时,浏览器会忽略除媒体类型之外的媒体查询条件
@import url(./min768.css) screen and (min-width: 768px) and (max-width: 1439px);
对于上面的媒体查询,低版本浏览器会理解为 @import url(./min768.css) screen,即只要媒体类型是screen,即应用min768.css,显然这是不对的。但是如果我们加上only限定,
@import url(./min768.css) only screen and (min-width: 768px) and (max-width: 1439px);
则此时低版本浏览器就会被排除在外,无法使用媒体查询暴露的样式。
如果想要制定复杂的媒体查询,可以参考MDN的使用媒体查询 - CSS(层叠样式表) | MDN (mozilla.org)
仿三星官网首页
响应式网页布局-仿三星官网首页-HTML5文档类资源-CSDN文库
在进行响应式网页开发过程中,我们会面临如下问题:
- 媒体查询的粒度问题
所谓媒体查询的粒度,即媒体查询成功匹配后,加载给网页的样式的是作用于整个网页的,还是作用于网页中某个局部组件的,比如:
上面例子中每个媒体查询的对应样式就是作用于整个网页的。
上面例子中,媒体查询只针对网页中某个局部组件。
如果从可维护性上来说,媒体查询的粒度越小越好,针对组件定义媒体查询,可以实现分工合作开发,并且发生问题时,可以根据问题所在组件来缩小问题范围,加速问题定位和解决。
如果代码复杂度上来说,媒体查询的粒度越小,则需要的媒体查询就越多,比如前面例子中,针对整个网页的媒体查询只需要4个,而针对组件的媒体查询可能需要 组件个数 x 4,这不仅浪费性能,而且非常容器造成各组件之间媒体查询条件定义不一致,产生混乱。
因此,媒体查询的粒度大小选择,需要根据实际情况来定。
- 媒体查询的样式复用问题
无论是针对整体网页的媒体查询,还是针对网页局部组件的媒体查询,都存在样式是否需要复用的问题。
比如三星官网首页的响应式布局中,视觉视口宽度≥1440px 和 视觉视口在[768px, 1439px]范围时,除了尺寸样式的尺寸等比例缩小外,其余样式均不变,这意味着两个媒体查询中存在大量重复的样式定义。
如果[768px, 1439px]媒体查询 复用了 [1440px, +∞)媒体查询 的样式,那么一旦 [1440px, ∞)媒体查询 的样式改变,则会直接影响[768px, 1439px]媒体查询 的样式。
如果不复用的话,媒体查询中则又会产生大量重复定义的样式,但是没有了耦合影响。
此时,我们其实需要关注两个媒体查询对应样式的关联程度,如果关联度高的话,则可以复用;如果关联度低的话,则不要复用。
什么是关联度?
即网页布局的变化是否很大,比如三星官网首页 [1440px, +∞),变为[768px, 1439px]时,网页布局并未发生较大变化,只是网页内容等比例缩小了而已,此时媒体查询之间就可以复用样式。但是三星官网首页从[768px, 1439px] 变为 [0, 767px] 时,网页布局发生了较大变化,差不多是从PC网页布局变为了移动端网页布局,此时,媒体查询之间就没有了样式复用的必要。