CSS 部分额外知识补充

· 4516 字 · 10 分钟 · 黄国政
......
<head>
......
  <title>Document</title>
  <link rel="" href="">
</head>
......

link 元素是外部资源链接元素,规范了文档与外部资源的关系,通常是在 head 元素之中。其最常用的链接是样式表(CSS),但也可以用来创建站点图标,比如 favicon 图标。

link 元素最常用的属性是 href [1]

[1] 回顾一下,imgiframe 元素中链接资源的属性都是 srclink 的是 href。此外,注意 link 元素虽可以引入 CSS 资源,但它是 HTML 中的元素,是标记语言,而非 CSS 这样的样式语言。 

,该属性指定被链接资源的 URL,这里的 URL 可以是绝对的,也可以是相对的;另一个常用属性是 rel,它指定 link 元素的链接类型——如前所述,包括 stylesheet(CSS 样式)和 icon(站点图标),更多类型可见此处

在此可以聊聊 link 元素中有助于网站实现性能优化的属性 dns-prefetch。此处只是看名称也可以尝试见名知意。过去我们在「网页的显示过程」中聊过 DNS 的含义,即域名解析服务器。

所谓域名解析服务器会将域名解析为对应的 IP 地址,进而找到对应的服务器,并将相关资源下载到浏览器中。但从域名解析为 IP 地址,这个过程需要花费一定的时间,因为 DNS 需要查询网站备案,而备案需要绑定对应服务器,这意味着默认的 DNS 解析需要经过查询备案和服务器的过程,最终才匹配到对应的 IP 地址。dns-prefetch 的功能正是预先执行 DNS 解析,这意味着我们下次再搜索相关域名时,该域名已经和对应的 IP 地址建立了映射关系,DNS 将不再需要花费时间进行解析。

如上图所示,京东网站的检查页面中就多次用到了 dns-prefetch 属性,这样可以让 DNS 将页面中的那些域名提前解析,待用户开始请求获取这些域名所对应的资源时,它们的 IP 地址已被获取,无须再解析。这种功能有利于网站的浏览,因为部分网站并非将所有的资源都放在同一个服务器,通过 DNS 的提前解析快速获取那些资源所在的 IP 地址,便能快速获取这部分资源。

二、计算机进制 #

2.1 简单介绍 #

进制这一概念来自数学,是一种记数方式,例如当数字达到某个值时,一位无法再表达,此时需要进一位。

最常见的是十进制,数字到 9 之后用一位无法再表示,此时便从一位变成两位——逢 10 便在 9 以前进一位得 1,原来的 9 归零,最后得到 10。除了十进制,还有二进制、八进制和十六进制。

所谓二进制只有 0 与 1,在 1 之后没有 2,用一位已经表示不了,要进一位,在 1 之前进一位得 1, 原来的 1 归零,得到的 10 表示 2。相同道理,在八进制中,7 以后没有 8,逢 8 在 7 前面进一位得 1,原来的 7 归零,得到的 10 表示 8;在十六进制中,由于 10 本身就是两位,因而在 9 之后,a 表示 10,b 表示 11,c 表示 12,d 表示 13,e 表示 14,f 表示 15,10 则表示 16。

从发明数字开始,人类就开始使用十进制了,这被认为可能与人类正好有十根手指有关,假设人类一开始只有八根手指,那么今天使用最广泛的是否应该是八进制——这意味着不会存在「8」这样一个数字,所谓的「8」会被「10」代替——而非十进制?由此我们或许可以推论,十进制并非放之四海而皆准的常理——二进制、八进制和十六进制便更符合计算机的运转逻辑。

计算机更偏好二进制与其底层原理密切相关,计算机内部主要还是电路的开关,如关闭用 0 表示,开启用 1 表示

在电脑的计算器程序中有一个程序员模式,里面显示着四种进制:

  • HEX(hexadecimal):十六进制,0x 开头,由数字 0-9 和字母 a-f 组成
  • DEC(Decimal):十进制,由数字 0-9 组成
  • OCT(Octonary):八进制,0o 开头,由数字 0-7 组成
  • BIN(binary):二进制,0b 开头,由数字 0 和 1 组成

在 Javascript 中可以利用 console.log() 来在浏览器的控制台中查看各进制转化为十进制的结果。

<script>
  console.log(99) // 打印的内容是十进制的数字,控制台显示的也是十进制结果
  console.log(0b01100011) // 打印的内容是十六进制的数字,控制台显示的是十进制结果
</script>

当然,虽然计算机偏好二进制,但是越高级的编程语言,越接近自然语言,因此在开发中用到的多是十进制,只是最后仍然要被转换回二进制。

2.2 进制转换 #

2.2.1 十进制 → 二进制 #

十进制数字除以 2,得到的整数保留,作为下一次除以 2 的被除数,直到整数部分为 0,每一次得到的余数都记下,最终逆序输出。

将十进制的 9 转换为二进制的 1001

92=41\frac{9}{2} = 4 \cdots 1

42=20\frac{4}{2} = 2 \cdots 0

22=10\frac{2}{2} = 1 \cdots 0

12=01\frac{1}{2} = 0 \cdots 1

2.2.2 十进制 → 八进制 #

十进制数字除以 8,得到的整数保留,作为下一次除以 8 的被除数,直到整数部分为 0,每一次得到的余数都记下,最终逆序输出。

将十进制的 9 转换为八进制的 11

98=11\frac{9}{8} = 1 \cdots 1

18=01\frac{1}{8} = 0 \cdots 1

2.2.3 十进制 → 十六进制 #

十进制数字除以 16,得到的整数保留,作为下一次 16 的被除数,直到整数部分为 0,每一次得到的余数都记下,最终逆序输出。但要注意,在十六进制中,a = 10,b = 11,c = 12,d = 13,e = 14,f =15,取得的余数若对应这几个数字,则需要转换回相应的字母。

将十进制的 999 转换为十六进制的 3e7

99916=627\frac{999}{16} = 62 \cdots 7

6216=314\frac{62}{16} = 3 \cdots 14

316=03\frac{3}{16} = 0 \cdots 3

2.2.4 二进制 → 十进制 #

将二进制数字拆开,有 n 个数字,第一个数字乘以 2n12^{n-1},余下的数字乘以的 2n12^{n-1} 部分中的 n - 1 则依次递减 1,最后全部相加即可。

将二进制的 1001 转换为十进制的 9
1×2n1+0×2n11+0×2n111+1×2n1111 1 \times 2^{n-1} + 0 \times 2^{n-1-1} + 0 \times 2^{n-1-1-1} + 1 \times 2^{n-1-1-1-1} (1×23+0×22+0×21+1×20)=8+1=9( 1 \times 2^3 + 0 \times 2^2 + 0 \times 2^1 + 1 \times 2^0 )= 8 + 1 = 9

2.2.5 八进制 → 十进制 #

将八进制数字拆开,有 n 个数字,第一个数字乘以 8n18^{n-1},余下的数字乘以的 8n18^{n-1} 部分中的 n - 1 则依次递减1,最后全部相加。

将八进制的 1234 转换为十进制的 668
1×8n1+2×8n11+3×8n111+4×8n1111 1 \times 8^{n-1} + 2 \times 8^{n-1-1} + 3 \times 8^{n-1-1-1} + 4 \times 8^{n-1-1-1-1} (1×83+2×82+3×81+4×80)=512+128+24+4=668( 1 \times 8^3 + 2 \times 8^2 + 3 \times 8^1 + 4 \times 8^0 )= 512 + 128 + 24 + 4 = 668

2.2.6 十六进制 → 十进制 #

将十六进制数字拆开,有 n 个数字,第一个数字乘以 16n116^{n-1},余下的数字乘以的 16n116^{n-1} 部分中的 n - 1 则依次递减1,最后全部相加。

将十六进制的 522 转换为十进制的 1314
5×16n1+2×16n11+2×16n111 5 \times 16^{n-1} + 2 \times 16^{n-1-1} + 2 \times 16^{n-1-1-1} (5×162+2×161+2×160)=1280+32+2=1314( 5 \times 16^2 + 2 \times 16^1 + 2 \times 16^0 )= 1280 + 32 + 2 = 1314
tip
十六进制的 522 对应十进制的 1314,或许这也是一种浪漫吧,如果 520 忘了过,可以过 522。

此处仅介绍整数部分从十进制转换到其他进制,以及其他进制转换到十进制的方法,小数部分,以及十六进制、八进制以及二进制之间的转换,可以见《进制转换(二进制、八进制、十进制、十六进制)超详细》。日后若有需求,我将再详细学习。

三、CSS 表示颜色 #

在 CSS 中,可以通过如下几种方式来表示颜色。

颜色关键字(color keywords)是不区分大小写的标识符,它表示一个具体的颜色,具体可以查询此处

color: red

但通过颜色关键字所能用的颜色实际上很有限,相对而言,RGB 颜色会用得更多。RGB 是一种色彩空间,通过 R(红色)、G(绿色)和 B(蓝色)三原色来组成不同的颜色,具体来说就是通过调整这三个颜色不同的比例来组合出其他颜色。

用 RGB 来表示颜色主要有两种方式,一种是以函数 rgb()rgba() 标记来表示,一种是以 # 为前缀的十六进制字符来表示——这也是前面要讲进制的原因。

方式一:函数符 rgb(R, G, B) #

color: rgb (red, green, blue)

函数中的 red、green、blue 可以是数字,也可以是百分比,数字最低取 0,最高取 255,因此数字中的 255 对应百分比中的 100%。当三个值都取 0 时,最后会呈现黑色(可以说黑色是最纯洁的颜色),相反,三个值都为最高的 255 时则是白色。

/* 黑色 */
color: rgb (0, 0, 0)
/* 白色 */
color: rgb (255, 255, 255)

rgba() 多了一个 a,代表透明度的含义,范围在 0 到 1 之间,也可以用百分比表示,1 对应 100%,它的具体写法如下:

color: rgba (red, green, blue, alpha)

方式二:十六进制符号 #RRGGBB #

但函数 rgb() 写法内容稍多,写起来或许会比较麻烦,此时可以通过以 # 为前缀的十六进制字符来书写。这种写法的逻辑是将原来函数 rgb() 中三种颜色的值从十进制转换为十六进制。例如我们将 color : rgb (0, 0, 0) 中的每个 0 都转换为对应的十六进制数字后,再以 # 作为前缀即可。

/* 黑色 */
color: #000000

color : rgb (255, 255, 255) 同理。

/* 白色 */
color: #FFFFFF

十六进制符号的写法也可以添加透明度设置,跟在末尾即可。

值得一提的是,如果是 #000000 或者 #FFFFFFAA 这样的形式,还可以分别缩写为 #000#FFFA 的形式。总结来说, #RRGGBB 可以缩写为 #RGB#RRGGBBAA 可以缩写为 #RGBA 的形式。

最后,在开发之中,以十六进制来表示颜色要比函数表示用得更多,主要使用前者。

四、Chrome 调试工具 #

稍微再补充一下在 Chrome 进行调试的内容,但不多:

  • 使用快捷键 Ctrl + + / - 可以调整页面或字体大小。
  • 直接在页面中的某个特定区域右键并选择检查,可以直接定位查看该区域的 HTML 结构。
  • 快捷键入 Ctrl + Shift + C 后,鼠标移动到哪个特定区域都会自动选中并显示其具体 HTML 结构。这个功能——选中元素——也可以在打开调试工具后点击左上角的的箭头实现。
  • 以上两种方式都可以查看具体的 HTML 结构,在此基础上,可以通过删除或添加某些元素来查看网页结构的变化。
  • 除了改变 HTML,还可以通过增删 CSS 来调试网页样式。

五、浏览器的渲染流程 #

这里对浏览器的渲染流程进行一个很简要的介绍,只涉及 HTML 和 CSS,尚未包括 JavaScript。

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>Document</title>
  <link rel="stylesheet" href="../css/style.css">
</head>
<body>
  
 <div class="home">
  <div class="title">
   <span>一段文本</span>
   <span>两段文本</span>
  </div>
  <h1>这是一级标题</h1>
 </div>

</body>
</html>

上面的代码包含了 HTML 结构和 CSS 样式两部分,一个基础网页的展示,最简要来说就是将这两部分交由浏览器来进行解析和渲染。接下来看看该过程的具体原理。

由上图可以看出,浏览器渲染的第一步是加载 HTML——过去我们说过,当我们访问某个网页时,首先会下载一份 index.html 的文件,这意味着浏览器首先获取的是 HTML 文件,先加载的也是 HTML(Load HTML),加载完成后开始从上往下地依次解析 HTML(Parse HTML),如从文档声明到 head 元素。

当解析到 head 元素时,往往会遇到 CSS 样式,因此需要讨论一下 CSS 的情况。如果 CSS 样式是以内联样式或内部样式表的形式存在于 HTML 中,那么这部分样式会与 HTML 一起被下载;但如果 CSS 样式是以 link 元素的形式从外部独立文件引入,浏览器此时便还需要从服务器中下载这部分 CSS,此时出现的问题是:这会儿浏览器是继续往下解析 HTML,还是等待 CSS 下载完毕后再继续解析 HTML?答案是不会等待,而是同时进行,即浏览器在加载和解析 CSS 时,仍然会独立地解析 HTML,两者互不干扰

head 元素到 body 元素,网页中往往可能会有一些复杂的嵌套结构(如上述代码),在浏览器解析完整个 HTML 结构后,浏览器会将整个结构转换为树形的结构,这就是 DOM Tree,如下所示。

HTML
├── head
│   ├── meta
│   ├── title
│   └── link
└── body
    └── div
        ├── div
        │   ├── span
        │   └── span
        └── h1

但转换出来的 DOM Tree 不会马上被渲染和展示,因为还不清楚 DOM Tree 中的每个节点(DOM nodes)有什么样式,只有 HTML 而还没 CSS,所以还要等待另一个工作流上的 CSS 被加载和解析完,然后给 DOM Tree 中的各节点(DOM nodes)依次附加上 CSS 样式,最后才统一渲染 [2]

[2] 一般来说都是如此,因为如果 DOM Tree 先被渲染了,待 CSS 解析后则还要再渲染一次,这样会影响性能。 

,由 DOM Tree 转换为 Render Tree,Render Tree 开始则可以真正开始进行渲染。换句话说,浏览器最终的目的就是获取 Render Tree,并根据 Render Tree 上的结构来进行最后的渲染和展示(Display)。

需要注意的是,往后还会讲到 JavaScript,因为在加载和解析 HTML 的过程中,JavaScript 可能会影响 DOM。