一、link 元素 #
......
<head>
......
<title>Document</title>
<link rel="" href="">
</head>
......
link 元素是外部资源链接元素,规范了文档与外部资源的关系,通常是在 head 元素之中。其最常用的链接是样式表(CSS),但也可以用来创建站点图标,比如 favicon 图标。
link 元素最常用的属性是 href [1]
[1] 回顾一下,img 和 iframe 元素中链接资源的属性都是 src,link 的是 href。此外,注意 link 元素虽可以引入 CSS 资源,但它是 HTML 中的元素,是标记语言,而非 CSS 这样的样式语言。
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,每一次得到的余数都记下,最终逆序输出。
2.2.2 十进制 → 八进制 #
十进制数字除以 8,得到的整数保留,作为下一次除以 8 的被除数,直到整数部分为 0,每一次得到的余数都记下,最终逆序输出。
2.2.3 十进制 → 十六进制 #
十进制数字除以 16,得到的整数保留,作为下一次 16 的被除数,直到整数部分为 0,每一次得到的余数都记下,最终逆序输出。但要注意,在十六进制中,a = 10,b = 11,c = 12,d = 13,e = 14,f =15,取得的余数若对应这几个数字,则需要转换回相应的字母。
2.2.4 二进制 → 十进制 #
将二进制数字拆开,有 n 个数字,第一个数字乘以 ,余下的数字乘以的 部分中的 n - 1 则依次递减 1,最后全部相加即可。
2.2.5 八进制 → 十进制 #
将八进制数字拆开,有 n 个数字,第一个数字乘以 ,余下的数字乘以的 部分中的 n - 1 则依次递减1,最后全部相加。
2.2.6 十六进制 → 十进制 #
将十六进制数字拆开,有 n 个数字,第一个数字乘以 ,余下的数字乘以的 部分中的 n - 1 则依次递减1,最后全部相加。
此处仅介绍整数部分从十进制转换到其他进制,以及其他进制转换到十进制的方法,小数部分,以及十六进制、八进制以及二进制之间的转换,可以见《进制转换(二进制、八进制、十进制、十六进制)超详细》。日后若有需求,我将再详细学习。
三、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 解析后则还要再渲染一次,这样会影响性能。
需要注意的是,往后还会讲到 JavaScript,因为在加载和解析 HTML 的过程中,JavaScript 可能会影响 DOM。