我们称电脑为 PC (personal computer),称手机、平板这种便携式设备为移动端。我们的网页有可能被 PC 和移动端两种设备打开,但很明显,PC 端和移动端就连屏幕尺寸都不一样,显示效果也必然是不一样的。可能一个在 PC 端显示效果很优秀的网页,一到移动端就出问题。
这里使用下面的代码测试一下
html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Document</title>
<style>
* {
margin: 0;
padding: 0;
}
</style>
</head>
<body>
这是一段文字
</body>
</html>
在 PC 端,这是非常正常的显示效果,但是如果我们调整到移动端

我们会发现字体小的可怜,要解释这个问题,就要引入视口的概念,先介绍一下如何设置视口,写法如下:
<meta name="viewport" content="width=device-width, initial-scale=1.0">
写上这个标签之后,移动端元素就会变得更大。
这个标签的具体细节先放一边,后面再说,读者在这只需要理解这个东西设置的是移动端视口大小即可

这个 viewport 的 meta 标签,设置的是移动端的视口大小(注意是移动端),视口就是我们能看到的区域的大小。在 PC 端,视口就是浏览器窗口的大小,我们知道 PC 端浏览器是一个窗口,是可以进行缩放的,我把浏览器缩小一点,视口就变小一点,把浏览器放大一点,视口就放大一点,如下图所示。值得注意的是,非根元素设置宽高为百分比时,是其父元素宽高的百分比,但 html 元素作为根元素没有父元素这一概念,如果 html 的宽高设置为百分比,基于的就是视口宽高。

接下来考虑移动端,在电脑我们很自由,可以随意修改窗口大小,移动端则不行,移动端的窗口大小是固定的,不能像电脑拿样随意改变大小。
PC 端视口大小其实就是窗口大小的像素值,这个很好理解,但移动端则不是简单的像素值,我们考虑这样一个问题,移动端有非常多的牛鬼蛇神,比如 A 手机和 B 手机屏幕大小相同,但是 A 手机一行有 1000 个像素点,B 手机一行有 2000 个像素点,那如果我让一个盒子的宽度为 1000 像素,那么它在 A 手机上能占满一行,但是在 B 手机上只会占满半行,这显然是会影响显示效果的。接下来就考虑如何解决这个情况。解决方案其实就是视口大小,比如我可以让 A 和 B 两个手机的视口宽度都为 1000 像素,此时 A 手机毫无疑问,B 手机由于屏幕宽度为 2000 像素,现在我设置视口大小为 1000 像素,那么 B 手机就会自动将两个像素点绑到一起,即将一行的两个像素合起来表示一个像素。这样就解决了这个问题。我们作为程序员,只要给他设置一个视口大小,手机屏幕就会按照这个视口大小来渲染,如果视口大小小于它屏幕的尺寸,他就会自动进行压缩,保证最终得到我们给定的视口大小,整个过程完全不需要我们操心。所以我们可以认为,设置视口大小之后,手机浏览器就会根据视口大小自行处理,哪怕对于超高分辨率的屏幕,浏览器也会自动模拟出我给定视口大小的显示效果。
以上我们介绍了什么是视口尺寸,视口尺寸本质上就是浏览器显示区的尺寸,在移动端我们可以通过 meta 标签指定视口尺寸,手机浏览器会识别这个标签,然后根据我的配置,模拟出我规定的尺寸。这样就解决了同尺寸移动端设备分辨率不一的问题。如果不去设置,大多数浏览器默认的移动端视口宽度是 980 像素,即在下图中,没有指定视口宽度,默认宽度就是 980 像素,也就是说不管屏幕是什么尺寸,最后模拟出的显示效果都 980 像素

而我们知道一个文字默认宽度只有 16 个像素,显示在 980 像素的屏幕上就会显得很小,这就是这里文字变小的根本原因。
我们之所以引入视口这个概念,本质上是希望在同样的屏幕尺寸下,网页的显示效果应该是相同的。什么意思呢,还是以 A 和 B 设备为例,A 设备宽为 1000 个像素,B 设备宽 2000 个像素,但是 A 和 B 的屏幕尺寸是相同的,只不过是 B 设备的像素更密,假设屏幕宽度都是 10 厘米,此时一个 1000 像素的盒子,如果不引入视口的概念,那 A 设备上就是 10 厘米,B 设备上就只有一半,也就是 5 厘米。引入视口之后,我让 A 和 B 的视口尺寸都是 1000 像素,此时 B 设备这 2000 像素的宽度就会按照我的设置自动模拟成 1000 像素的显示效果,这个时候就保证了 A 和 B 两个设备的显示效果一致。
那对于不同尺寸的设备呢?比如说我这里有一个宽度 20 厘米,一行 3000 像素的平板 C。如果将 C 的视口宽度也设置为 1000 像素,那他就会用 3000 像素的宽度去模拟 1000 像素的显示效果,比如显示一个 1000 像素宽度的盒子,在 A 和 B 设备上能够占满一整行,而此时在 C 上,由于 C 模拟的是 1000 像素的显示效果,所以也会占满一整行,实际上在平板 C 上就相当于把网页放大了一倍。这是非常不合理的,还原为 C 的屏幕宽度是 A 和 B 的两倍,理应给用户显示更多的内容,而不是单纯的放大,所以对于不同尺寸的设备,我们其实并不希望将视口宽度设置为同样的值,从这个例子我们也能看出,视口大小应该和屏幕尺寸呈正比,屏幕尺寸越大,就应该显示更多的内容,视口就应该更大。屏幕尺寸越小,就没有能力把那么多内容显示得那么清楚,所以视口也就应该设置的很小。
注意区分概念,这里屏幕尺寸是屏幕真实的大小,也就是用尺子量出来的大小,屏幕像素有时候也可以叫做屏幕尺寸,单位是像素点。
我以字体举例,我假设字体大小都为 16 像素,我将 A 和 B 的视口宽度设置为 160 像素,这个时候 A 和 B 不管分辨率是多少,都要去模拟 160 像素的显示效果,也就是一行能显示 10 个字。A 和 B 设备的宽度都是 10 厘米,也就是说一个字宽度为 1 厘米。此时如果让 C 的视口宽度为 160 像素,C 就会去模拟 160 像素的显示效果,一行也是 10 个字,但是 C 的屏幕宽度是 20 厘米,这个时候一个字的宽度就变成了 2 厘米。我们其实更加希望同样一个盒子在不同尺寸的屏幕上显示的宽高是差不多的,比如这个字在 A 和 B 设备上显示为 1 厘米,那在 C 设备上也应该显示为 1 厘米,更大的屏幕可以用来展示更多的信息,而不是这样无意义地把字变得很大,这样才是一个比较理想的效果,所以当 C 设备在访问我网页的时候,我希望将视口设置为 320 像素,这样一行就可以显示 20 个字,每个字宽度就是 1 厘米。
屏幕越大意味着可以用来显示更多的信息,举个例子,我可以实现这么一个效果,手机打开我页面的时候,展示一列,平板打开的我页面的时候,由于屏幕尺寸变大了,可以变成两列布局,至于具体怎么实现的会在后面媒体查询介绍,这里只需要知道,面对更大尺寸的屏幕,我们可以为其适配,让其才显示更多的内容,而不应该做毫无意义的等比例放大。
由于我们程序员开发其实是基于视口尺寸的,所以视口尺寸也可以称为开发尺寸。注意,虽然他们数值一样,但是侧重点不一样,开发尺寸是站在程序员视角的,视口尺寸是站在用户视角。
我们已经知道了,对于同一个尺寸的设备,为了消除他们之间的像素差异,让他们显示相同的效果,应该给他们设置同一个视口尺寸。而对于屏幕更大的设备,屏幕更大意味着可以容纳更多的信息,所以我们应该设置一个更大的视口尺寸。既然视口尺寸和屏幕尺寸呈正比,那什么才叫合适呢,或者举例来说就是我现在有一个 7 英寸的设备,视口大小设置为多少才合适呢?其实这个不需要我们操心,移动端设备出厂的时候,厂商就给它了一个数值,叫做物理像素比,我们将这个设备的真实尺寸除以物理像素比,得到的就是开发尺寸,或者说视口尺寸,下面是部分设备的开发尺寸。

我们这里以苹果 4 和 ipad mini 为例,苹果 4 屏幕的物理像素为 640*960,其物理像素比为 2.0,所以视口尺寸为 320*480。ipad mini 物理像素为 768*1024 ,物理像素比是 1.0,所以视口尺寸为 768*1024。
物理像素比是移动端设备出厂自带的参数,不需要我们操心。
以上介绍了所有关于视口相关的概念,接下来我们回到最开始那个放下的问题,就是如何使用 meta 标签来设置视口。
如果不使用
meta元素设置视口尺寸,默认的视口宽度通常是 980 像素。
html
<meta name="viewport" content="width=device-width, initial-scale=1.0">这是最常见的写法,使用 vscode 会自动生成这一行代码,很多程序员也根本不知道这行代码是啥意思,这里就介绍一下。
首先,meta 元素是设置元数据,它可以用来设置很多东西,比如编码等等,我们必须先告诉它我们这里设置的是视口,这里的 name="viewport" 就是在告诉这个 meta 元素,我这里是在设置视口。
接下来具体的设置参数写在 meta 元素的 content 属性里面,多个属性之间用逗号隔开。最常见的属性就是 height 和 width,也就是设置视口宽高。比如我们看上面的表,这里面苹果 4 的开发尺寸是 320*480,所以我们可以这么写:
html
<meta name="viewport" content="width=320, height=480">如果读者这么写的话,效果如下

可以看到这里其实就正常了,需要注意的是,html 元素的默认宽度是 100%,是基于视口宽度的 100%,所以视口宽度设置为多少,html 元素的宽度就应该是多少。
但是这里有一个问题,我这里把像素给他写死了,这肯定不好对吧,因为这里让视口宽度为 320 像素,那不管什么设备来看我这个网页都是 320 像素的视口宽度,这不是我们想要的,因为不同设备的屏幕尺寸并不相同,我们希望的是,一个设备来看我这个网页,我根据不同的设备可以设置不同的视口尺寸,这才是我们想要的,这也是我们可以办到的,写成下面这个值即可。
html
<meta name="viewport" content="width=device-width">这个 device-width 其实就是设备的开发尺寸的宽度,是通过设备真实尺寸除以物理像素比算出来的。当我们这样设置之后,视口宽度就会设置为该设备真实的宽度除以物理像素比,也就是说当我们填上这个值的时候,网页的视口宽度就会根据设备自适应,不需要我们再设置。
通常我们不设置高度,让高度去自适应
我们通常还会写上 initial-scale=1.0,这个属性其实就是设置缩放比例为 1 倍,效果和 width:device-width 可以说是一模一样,以下就是 vscode 生成的写法:
html
<meta name="viewport" content="width=device-width, initial-scale=1.0">为什么会同时写上 width 和 initial-scale 两个属性
width:device-width 和 initial-scale=1.0 效果其实是一样的,同时写上其实是保证兼容性问题。
在 iphone 和 ipad 上,device-width 表示的是竖屏状态下的宽度,也就是说如果我把屏幕横过来,视口宽度也还是竖屏状态的宽度,这是不合理的,所以加上 initial-scale:1.0 解决这个问题。
在 windows phone 中的 ie 浏览器上,设置 initial-scale=1.0,此时如果把手机横过来,视口宽度也是竖屏状态的宽度。
正因为 initial-scale 和 device-width 都存在兼容性问题,所以要同时写上。
以上的 width 和 initial-scale 几乎是必写的,也是 vscode 会自动生成的,但也有一些可选的属性,这里简单介绍。
读者可能在浏览一些手机网页的时候,会发现有的手机端网页是可以缩放的,有的手机端网页则不允许缩放,比如双击,比如双指滑动,都可以进行缩放,但用户缩放我的网页可能会导致一些布局上的问题,所以很多时候我不允许用户来缩放我的网页,这个时候就可以使用 user-scalable 属性,将改属性设置为 no,就可以禁止用户缩放我的网页。
那如果我允许缩放,那也不能允许用户毫无底线地缩放吧,比如把尺寸放的特别大或者特别小,这是不被允许的,所以我们可以使用 minimum-scale 和 maximum-scale 来设置最小和最大的缩放比例,当然,如果我设置 user-scalable 为 no,直接不允许用户缩放,这两个属性也就没用了。
以上介绍了所有关于视口的概念,接下来就是移动端网页的开发,我有一个网站,如果要同时适配电脑端和移动端,或者说不管是 PC 还是移动端,只要访问我的网站,都想要达到不错的效果,读者可以看一下 PC 端的网站,如果将这些网站等比例缩小,放到手机上去,它的字体会变得非常非常小,我们需要缩放才能看得清楚,这显然并不友好,为了让我的网站同时兼容 PC 端和移动端两种设备,很容易想到的方案是开发两套网页,一套是 PC 端,一套的移动端,当用户访问我网站的时候,先检测用户是移动端还是 PC 端,然后展示不同的页面,很多网站都使用了这个方案,例如百度,如果用移动端访问 https://www.baidu.com,会自动跳转到 https"//m.baidu.com,很显然,这个 https://m.baidu.com 的网站就是百度专门为移动端设计的,当我们使用移动端访问百度的时候就会自动跳转到这里。
但是这种方案也存在缺陷,一个显而易见的缺陷就是开发成本问题,我们需要开发和维护两个网页,必然会提升维护成本,为了压缩成本,我们就引入了另一种想法,就是我只做一个网页,大屏设备和小屏设备打开我的网页显示不同的样式,这就是响应式布局。可以看到响应式布局的核心就是根据不同的屏幕尺寸显示不同的样式,这个其实是可以做到的,举个例子,我们知道 body 的宽度是基于 html 元素的 100%,而 html 元素是基于视口的 100%,我们可以在 body 下面设置一个宽度为 50%的盒子,这个时候当我们屏幕宽度为 1000 像素的时候,盒子宽度就是 500 像素,当屏幕宽度为 500 像素的时候,盒子宽度就是 250 像素,显然这里就做到了根据不同的屏幕宽度显示不同的尺寸这样一个效果。
我们要开发一个响应式网页主要会用到以下方法
- 流体布局 :宽高不固定的布局,可用 flex,也可用百分比布局。
- 弹性图片:图片宽高设置为百分比。
- 媒体查询:专门用来做响应式网页的一个技术,使用媒体查询可以在不同屏幕尺寸(视口宽度)的设备上展示不同的 CSS 样式。
- 主要断点:这个断点就是媒体查询的断点,屏幕尺寸有非常多,我们不可能去适配所有尺寸,一般会划分区间去做适配,可以认为是设备宽度的临界点。举个例子,假设现在两个设备的视口宽度分别是 600 和 610 像素,很明显这两个宽度差不多,没有必要去单独适配,可以使用同样的 CSS 样式,所以我可以认为 576 到 768 像素的视口宽度均为小屏幕设备,都使用一套 CSS,而 768 到 992 像素为大屏幕设备,使用另一套 CSS 样式,这里的 768 像素就是小屏幕设备和大屏幕设备的断点。
媒体查询这种根据屏幕尺寸展示不同样式的效果,通过 JS 去监听窗口尺寸也是可以做到的,但是会有一定性能问题,而且这其实属于样式的范畴,我们追求结构样式行为相分离,所以都使用媒体查询去完成。
接下来详细介绍媒体查询,媒体查询就是根据不同视口宽度显示不同样式的技术,我们不可能针对所有屏幕尺寸都去设置一个样式,这样工作量太大,也没有必要,比如一个视口的宽度为 500 像素,一个视口的宽度为 501 像素,我难道还要为这两个差距一像素的屏幕宽度单独设置样式吗,显然非常没必要,所以我们会将屏幕尺寸进行分类,可以分成小屏幕,中等屏幕,大屏幕等等类型的屏幕尺寸,而这些屏幕尺寸的临界点就是前面提到的断点。这些邻接点我们完全可以自己定义,也可以参考 bootstrap 框架的屏幕尺寸分类,如下表所示
| 超小设备 <576px | 平板 ≥576px | 桌面显示器 ≥768px | 大桌面显示器 ≥992px | 特大桌面显示器 ≥1200px | 超大桌面显示器 ≥1400px |
|---|
这里的像素值均为视口宽度。再次注意,当设置
width=device-width之后,屏幕尺寸和视口宽度呈正比。
媒体查询并不是 CSS 3 新概念,在 CSS 2 里面就有媒体查询的概念,CSS 3 对媒体查询进行了加强,最显著的加强就支持设置断点,这直接让媒体查询有能力做响应式网页的开发。
媒体查询其实就是设置一些条件,引入指定的 CSS 样式,这些条件分为两类,媒体类型和媒体特性,媒体类型是 CSS 2 就有的概念,而媒体特性是 CSS 3 新加的。以下介绍如何使用媒体查询。
html
<link rel="stylesheet" media="screen and (max-width:375px)" href="index.css">以上媒体查询的意思就是,如果目前是电脑屏幕,并且视口的宽度小于 375 像素的时候,使用 index.css 这个样式。这里的 screen 指的是电脑屏幕,也可以用 print,指的就是打印机,这属于媒体类型。而后面的 (max-width:375px) 是媒体特性,媒体特性必须用小括号括起来。媒体类型和媒体特性之间可以使用 and 连起来。
除了这种写法以外,我们还可以使用以下写法将媒体查询直接写在 CSS 文件中:
css
@import 'index.css' screen and (max-width:375px);css
@media screen and (max-width:375px) {
/*
样式
*/
}媒体查询内部的样式并不提升权重,例如媒体查询内部给
html元素设置了样式,媒体查询外部也给html设置了样式,那就得看谁在前在后了。所以一般媒体查询放在 CSS 的最后,这样可以让同权重的选择器使用媒体查询内部的样式。
css
@media screen and (max-width:375px) and (min-width:300px) {
/*
样式
*/
}and 操作符可以连接多个媒体类型和媒体特性,例如上面的媒体查询就是设置视口宽度在 300 到 375 像素的特有样式。
媒体查询并没有
or操作符,如果真的要使用or操作符,可以考虑写多个媒体查询,也可以考虑使用逗号隔开,读者可以简单把逗号理解为或。
媒体查询有not操作符,如下:not screen and (max-width:375px),表示除了这个条件以外的所有媒体,都使用该样式。
only 关键字
除了 and 和 not 以外,还有一个 only 关键字,在 CSS 2 中,媒体查询只支持查询媒体类型,也就是判断是打印机还是屏幕,根据不同的媒体类型适用不同的样式。在 CSS 3 引入了媒体特性,也就是可以根据屏幕尺寸来区分不同的类型,例如 (min-width:300px) 这个媒体特性表示的就是最小宽度为 300 像素的设备,即视口宽度大于等于 300 像素的设备。而现在就有一个问题,就是早期版本的浏览器并不支持 CSS 3 中的一系列媒体特性,如果我们写上了 only,就可以解决这个兼容性问题,加上 only 关键字后,如果浏览器无法识别媒体特性,就只会识别媒体类型,不会出现兼容性问题。
在响应式网页开发的时候,我们会使用媒体查询将设备分割为很多种类,比如小屏幕,大屏幕等等,那问题是,小屏幕也有一定区间呀,比如 570 像素和 700 像素我都可以认为是小屏幕,那我显示一个 200 像素宽度的图片,在 570 像素的屏幕上占 35% 的宽度,在 700 像素的屏幕上占 28% 的宽度,所以它们最终的布局也是不一样的,所以我们并不会使用像素这种绝对单位,而是使用一些相对单位。以下是常用的单位
- rem:
1rem是 html 元素的font-size值 - em:
1em是该元素父元素的font-size值 - px:
1px是 CSS 的逻辑像素,注意,1 px 代表的可能并不是真实的 1 像素,这一点在视口那里介绍过,有可能 CSS 的 1 像素显示到真实的物理设备上是好几像素。 - vw:相对视口宽度,
100vw表示 100%的视口宽度,1vw表示 1%的视口宽度 - vh:相对视口高度,和 vw 类似。
- vmax:vmax 是 vw 和 vh 中更大的那一个,比如此时视口宽度为 1000 px,视口高度为 500 px,那么 vmax 和 vw 是等价的。
- vmin:vmin 是 vw 和 vh 中更小的那一个,比如视口宽度为 500 px,视口高度为 400 px,那么 vmin 和 vh 是等价的。