动画: 一个浏览器是如何工作的?
设为 “星标”,每天教你学编程!
作者 | 小鹿
来源 | 小鹿动画学编程
写在前边
对于面试的大部分前端开发者来说,对浏览器的了解也算是一知半解,因为我们一开始前端觉得,我们开发中通常使用浏览器来显示和调试页面用的,不会涉入到太过的相关浏览器工作原理知识。
这你就大错特错了,虽然浏览器默默的为我们工作,但是有关浏览器的工作原理不但在你就业前端面试中属于重中之重,也是在前端优化中占有很大比重。作为一个前端开发如果不了解浏览器的工作原理,只能永远停留在前端“切图仔”水平。
PS:前几天还有个读者和小鹿说,之前看到小鹿朋友圈发过这个这个导图,当时没当回事,第二天面试竟然问到了。
学习浏览器工作原理是为了能够运用到实际项目中,比如前端的性能优化以及错误排查,都会涉及到浏览器相关的知识,所以掌握浏览器的工作原理是必不可缺的,相信你学完之后,能够对你的个人能力和见识会有很大的提升。
浏览器涉及到的知识点非常多而且非常重要,文章中可能其中有不足和错误的地方,也欢迎各位指出!
思维导图
1
浏览器的职责
我们由浅入深的去了解浏览器的工作原理。首先学习一个事物要知道它是什么,它要完成一个什么样的事情,也就是拥有什么样的职责。
对于浏览器来说,表面上看来,我们输入了 URL,然后等待几秒浏览器就展现出我们想要访问的网址内容了。对没错,这就是浏览器的职责所在。这只是停留在了表面,要想深入知道浏览器在这个阶段发生了什么?需要我们进一步探索浏览器的工作原理。
所以说,无论是面试还是实际工作中,浏览器无时不刻和我们打交道。那我们就要从在浏览器输入 URL 开始说起,直到浏览器最后展现出网站内容,这个过程浏览器做了哪些工作,又是如何工作的呢?
我们会详细分为以下几大模块进行系统的讲解:
1、DNS 解析
2、TCP 连接
3、HTTP 请求
4、构建 DOM 树
5、构建 CSSOM 树
6、生成渲染树
7、合成、绘制
其中有些部分我们之前的文章已经分享过,所以也不过到陈述,到时候会巩固一下,重点我们放在浏览器的渲染原理上,这也是我们以后在学习前端优化中的重点。
2
DNS 解析
在之前的文章 TCP 三次握手中提到,要想得到接收方的 MAC 地址,需要通过对方的 IP 地址获取,对方的 IP 需要通过 DNS 解析。
2.1
为什么进行 DNS 解析?
所谓的 DNS 解析就是将我们输入在网页地址栏的 URL 通过 DNS 解析成 IP 地址。DNS 就是将域名转化成 ip 地址的过程。那么这个过程会发生什么呢?和小鹿一起深入探究一下。
2.2
系统缓存查询
首先浏览器会调用一个库函数,检测本地的 hosts 文件(可以认为是电脑本地的一个地址映射文件),从该文件中查看是否有对应的该域名的 IP 地址,这个过程是在系统缓存中查找是否存在该域名对应的 IP 地址。
比如在浏览器的地址栏中输入小鹿的博客网址(www.xiaolu.com),然后回车,此时浏览器拿着这个域名去本地电脑的一个名为 hosts 文件中查询是否存在该域名所对应的 IP 地址,如果有,就返回给浏览器,如果没有,我们继续往下进行。
2.3
路由器缓存、ISP 缓存
如果系统缓存没有,就会向 DNS 服务器发送请求,而网络服务一般都会先经过路由器以及网络服务商(电信),所以会先查询路由器缓存,然后再查询 ISP 的 DNS 缓存。
PS:ISP缓存,本身是一种宽带接入提供商给网页批量访问加速的技术。ISP会将当前访问量较大的网页内容放到ISP服务器的缓存中,当有新的用户请求相同内容时,可以直接从缓存中发送相关信息,不必每次都去访问真正的网站,从而加快了不同用户对相同内容的访问速度,同时也能节省网间流量结算成本。
2.4
DNS 递归查询
如果路由器缓存和 ISP 的 DNS 缓存还是没有的话,我们就进行 DNS 递归查询。从根域名服务器开始查询,然后再到顶级域名服务器,最后到主域名服务器依次查询。
但是这里有两种查询方式,不仅仅有递归查询一种方式,还有一个查询方式是迭代查询,两种查询方式的区别是什么呢?
迭代查询:DNS 收到请求时,而不是直接返回查询结果,而是告诉客户端另一台 DNS 服务器地址。然后客户端再向这台的 DNS 服务器提交请求,依次循环。
递归查询:当 DNS 服务器收到请求时,就会检查 DNS 缓存,如果没有就会询问其他服务器,并将返回的查询结果返回客户端。
我们前端会在性能优化的使用用到 DNS 的相关知识,我们在这稍微提一下,如何进行 DNS 优化呢?
DNS 查询经历很多步骤,查询很慢。浏览器获取到 IP 地址后,一般都会加到浏览器的缓存中,本地的 DNS 缓存服务器,也可以去记录。
另外使用 DNS 负载均衡,通常我们的网站应用各种云服务,DNS 系统根据每台机器的负载量,地理位置的限制等等,去提供高效快速的 DNS 解析服务。
3
TCP 连接
我们通过 DNS 查询到 IP 地址之后,我们就开始打算与服务器建立连接,为接下来的数据传输做准备,这部分在之前的HTTP文章有捎带讲解!
4
HTTP 请求
我们客户端与服务端通过 TCP 的三次握手建立连接之后,客户端开始向服务器主动发起请求。
服务端接收到客户端发送的信息,就返回响应信息和文件。客户端如何判断服务端是否成功返回了呢?就需要下列的一些状态码来识别,同样前端做的工作也是通过状态码来判断当前响应状态。
1XX(信息性状态码) : 服务器正在处理请求中。
2XX (成功状态码): 请求处理完毕。
3XX (重定向状态码): 需要附加操作以完成请求。
301:永久性重定向。该状态码表示请求的资源已被分配了新的 URI,以后使用该资源,使用现在所指 URI。
302:临时性重定向。表示该状态码被分配了新的 URI,希望用户本次能够使用新的 URI 访问。
304:服务器资源未改变,可直接使用客户端未过期的缓存。
4XX (客户端错误状态码): 服务器无法处理请求。
400:该请求报文中有语法错误。
403:没有资源的访问权限。
404:找不到资源。
5XX (服务端错误状态码): 服务器处理请求出错。
500:服务器发生错误
503:服务器超荷载或正在维护。
远程服务器找到资源并使用 HTTP 响应返回该资源,值为 200 的 HTTP 响应状态表示一个正确的响应。
5
浏览器的渲染原理
5.1
构建 DOM
服务器将 HTML、CSS、JS文件转化为 0,1字节数据在网络中传输给浏览器,浏览器通过判断状态码开始接收、解析文件,这开始运用到浏览器的渲染原理。
首先浏览器要做的就是获取 HTTP 的 Request 的 body 中字符串(字符流)的 HTML 文本,进行解析并构建 DOM 树。
将字符流转化为字符串之后,浏览器开始进行词法分析,虽然这个名词我们不熟悉,但是我们要知道,一个 HTML 字符串我们要拆分开才能构建 DOM 树,词法分析就是将字符串拆分成的过程。将字符串转化为的 token(标记) —— token 作为代码的最小单位,也就是拆分后的结果,这个过程我们称为标记化。
我们将字符串拆解之后,然后将这些标记转化为 Node 结点,浏览器根据不同的结点开始构建一棵 DOM 树。这就是整个 DOM 树构建的过程,其中还涉及到很多的细节,比如词法分析是如何一个过程(状态机),有兴趣的小伙伴可以详细查看英文文档,在文章底部。
5.2
构建 CSSOM 树
浏览器已经把 HTML 文件转化为了 DOM 树,下面就对 CSS 样式文件进行解析,构建成 CSSOM 树。这个过程和上述构建 DOM 树的过程有点相似,但是其中 CSSOM 树的构建更加的耗时。下面我们来看看如何耗时的?
浏览器通过递归的方式 DOM 树为结点设置样式。通过先找到具体的标签,然后递归找到设置的上级标签,最后确定选择器选择的所选标签的样式。
比如下边的例子,浏览器是如何确定结点的样式的呢?
1 <div>
2 <span>
3 <p>小鹿动画学编程,一天一篇动画喂饱你!</p>
4 </span>
5 <p>你好,小鹿!</p>
6 </div>
7
8
9 div span p{
10 color: red;
11 font-size: 12px;
12 }
13 div {
14 background:red;
15 }
首先在 HTML 中找到 p 标签,一共有两个地方,然后按照样式的继续递归中找具有父节点的 p,我们只好把第二个 p 过滤掉,然后继续向上找父节点 div,匹配成功,然后将样式设置到结点上。
我们通过上边的动画,可以知道为什么构建 CSSOM 树的时候非常耗时了,我们在写代码的时候可以做出优化,所以应该避免书写过于具体的 CSS 选择器,少一些添加无意义的 HTML 标签,有利于提高页面的性能。
5.3
构建渲染树
我们分别将上边生成的 DOM 和 CSSOM 树进行合并,生成我们的渲染树。但是在合并的时候,并不是两者简单的进行结合,因为有些结点我们并不需要显示,还记得有一个 display:none 属性吗?如果某结点的样式有这个属性,就不会出现在渲染树中。
5.4
合成、绘制
浏览器在生成渲染树的时候,就会根据渲染树进行布局,调用 GPU 进行绘制,然后合成图层,最后显示在屏幕上。
小结
通过上边的对浏览器工作原理的介绍,相信你对浏览器有了新的认识和简介,但是只看上边的知识还完全不能深入到底层,如果还想要进行深入的对浏览器的工作原理进行研究,可以上网找找《How Browsers Work》,已经有人把它翻译成了中文。
希望你看完本篇文章对浏览器工作流程有个大概的了解!
推荐阅读:
面向回家编程!GitHub 标星两万的 "Python 抢票教程”,我们先帮你跑了一遍