一、link 元素 #
......
<head>
......
<title>Document</title>
<link rel="" href="">
</head>
......
link 元素是外部资源链接元素,规范了文档与外部资源的关系,通常是在 head 元素之中。其最常用的链接是样式表(CSS),但也可以用来创建站点图标,比如 favicon 图标。
link 元素最常用的属性是 href1,该属性指定被链接资源的 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,每一次得到的余数都记下,最终逆序输出。
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 个数字,第一个数字乘以 $2^{n-1}$,余下的数字乘以的 $2^{n-1}$ 部分中的 n - 1 则依次递减 1,最后全部相加即可。
2.2.5 八进制 → 十进制 #
将八进制数字拆开,有 n 个数字,第一个数字乘以 $8^{n-1}$,余下的数字乘以的 $8^{n-1}$ 部分中的 n - 1 则依次递减1,最后全部相加。
2.2.6 十六进制 → 十进制 #
将十六进制数字拆开,有 n 个数字,第一个数字乘以 $16^{n-1}$,余下的数字乘以的 $16^{n-1}$ 部分中的 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,由 DOM Tree 转换为 Render Tree,Render Tree 开始则可以真正开始进行渲染。换句话说,浏览器最终的目的就是获取 Render Tree,并根据 Render Tree 上的结构来进行最后的渲染和展示(Display)。
需要注意的是,往后还会讲到 JavaScript,因为在加载和解析 HTML 的过程中,JavaScript 可能会影响 DOM。