共计 7595 个字符,预计需要花费 19 分钟才能阅读完成。
解析地址栏中的信息
浏览器监听用户输出的信息并尝试匹配你想要拜访的网址或关键词。以掘金为例,在浏览器地址栏中输出信息,而后回车,浏览器会进行以下判断:
- 判断是否是非法的 URL 链接;
-
是。持续判断 URL 是否残缺,如果不残缺,浏览器可能会对域进行猜想,对输出的内容增加前缀、后缀、或者前后缀来补全 URL,常见的 URL 通产包含:
- 协定:如
http
https
websocket
- 域名(主机名):可能是 IP 地址,也可能是域名。域名可能由根域名、顶级域名、二级域名等组成,域名的叫法是依据域名从右向左以
.
分隔进行划分,比方:juejin.cn.
,.
代表根域名,.cn
代表顶级域名,juejin.cn
代表二级域名(也就是主机名) - 端口号:http 协定默认端口号为 80,https 协定默认端口号为 443。浏览器会自动隐藏默认端口号。
- 门路:以
/
划分每一层目录,比方:/web/user
- 查问:以
?
开始,以&
分隔键值对,如:?username="张三"&age=16
- 哈希:以
#
开始,利用它可实现定位到以后页面的具体位置
- 协定:如
- 否。浏览器将输出的内容作为搜寻条件,应用用户设置的默认搜索引擎进行查问并返回后果
查找强缓存
浏览器过程通过过程间通信(IPC)将 URL 申请发送给网络过程,网络过程接管到 URL 申请后,会发动真正的申请。但在申请之前,网络过程会查找本地是否缓存了该资源。如果有缓存资源,那么间接返回资源给浏览器过程。首选,查找强缓存资源,如果有则查看强缓存资源是否过期,没过期间接应用该资源,过期则从新向服务器申请资源。强缓存波及到两个字段:
Expires
。即过期工夫(Expires=Wed, 21 Oct 2015 07:28:00 GMT)
,HTTP/1.0 采纳此字段,它存在于服务器返回的响应头中,告知浏览器在过期工夫范畴内间接应用缓存资源。但它有个很大的毛病,当服务器和客户端的工夫不统一时,那么服务器返回的工夫是不精确的,因而,HTTP/1.1 摈弃了这个字段而采纳了Cache-Control
字段
-
Cache-Control
。即过期时长(Cache-Control:max-age=3600)
,HTTP/1.1 采纳此字段,它也存在于服务器返回的响应头中,告知浏览器在过期时长范畴内间接应用缓存资源。它还能够设置其余指令,上面列举一些要害指令:public
。浏览器和代理服务器都能够缓存资源private
。只能浏览器缓存资源,代理服务器不能缓存资源no-cache
。跳过强缓存阶段。向服务器发送申请,进入协商缓存阶段no-store
。不缓存s-maxage
。代理服务器的缓存工夫must-revalidate
。一旦缓存过期,就必须回到源服务器验证
扩大:
- 怎么设置强缓存?能够在服务端代码中设置
Cache-Control
字段以及他对应的值; - 强缓存资源缓存在哪儿?
memory cache
或disk cache
,也就是内存或硬盘中,个别会将图片、脚本文件、字体文件缓存在memory cache
中;将款式文件缓存在disk cache
中。 - 拜访缓存的优先级?遵循三级缓存原理:先在
memory cache
中找,有则间接应用;没有再去disk cache
中找,有则指间接应用;没有就进行网络申请,将申请返回的资源依据响应头字段信息进行缓存。
DNS 域名解析
如果在强缓存中没有找到所需资源,那么间接进入网络申请流程。通常状况下,咱们在浏览器的地址栏中输出的都是域名,而在网络通信中是以 IP 地址确定目标主机的,所以还得通过域名找到对应的 IP 地址。
DNS 又是什么?DNS 全名是 domain name system(域名零碎)
,它将域名和 IP 地址映射关系保留在一个分布式数据库中,所以咱们能够通过 DNS 找到对应的 IP,而这个查找的过程就是 DNS 域名解析。上面以 juejin.cn.
来剖析域名的解析过程:
- 浏览器 DNS 缓存。浏览器从 URL 中提取出主机名,从浏览器 DNS 中查找是否有缓存记录,有则间接应用缓存 IP,实现解析;
- hosts 文件。从本机的 hosts 文件中查找是否有缓存记录,有则返回对应 IP,实现解析;
- 本地 DNS 服务器。向本地 DNS 服务器发送查问申请,有则本地 DNS 服务器将记录作为响应返回给主机,实现解析;
- ISP(互联网服务提供商)DNS 缓存。本地 DNS 服务器将查问申请转发给 ISP 提供的 DNS 服务器,有则将记录作为响应返回给本地 DNS 服务器,本地 DNS 服务器返回给主机,实现解析;
- 根域名服务器。依据本地 DNS 服务器的设置(是否设置转发器)进行查问,若是未用转发模式,本地 DNS 就把申请发至 13 台根域名服务器,根域名服务器收到申请后会判断这个域名(
.cn
)是谁来受权治理,并会返回一个负责该顶级域名服务器的一个 IP。若是转发模式,该 DNS 服务器就会把申请转发至上一级 DNS 服务器,由上一级服务器进行解析,上一级服务器如果不能解析,或找根域名服务器或将申请转至上下级,以此循环; - 顶级域名服务器。从根域名服务器失去顶级域名服务器地址后,本地 DNS 服务器向顶级域名服务器发送查问申请,收到本地域名服务器申请后会查看区域文件记录,有则将记录作为响应返回给本地 DNS 服务器,由本地 DNS 服务器返回给主机,实现解析。如果本人无奈解析,它就会找一个治理(
.cn
)域的二级域名服务器 IP 返回给本地 DNS 服务器; - 二级域名服务器。从顶级域名服务器失去二级域名服务器地址后,本地 DNS 服务器向二级域名服务器发送查问申请,二级域名服务器收到本地域名服务器申请后会查看区域文件记录,若有则将记录返回给本地 DNS 服务器,实现解析。到这一步还是没能实现解析,那域名可能存在谬误而产生异样。
递归查问:
客户端向本地 DNS 服务器发动查问申请,期待本地域名服务器返回后果。本地 DNS 服务器若无奈解析,本人会以 DNS 客户机的身份向其余域名服务器发动查问申请,直到将查问后果返回给客户端为止。
迭代查问:本地 DNS 服务器首先向根域名服务器发动查问申请,若根域名没有找到对应记录就将下一个指标域名服务器的 IP 返回给本地 DNS 服务器(也称为根提醒),直到将查问后果返回给客户端为止。
建设 TCP 连贯
建设连贯遵循三次握手准则。三次握手的次要目标就是确认单方的接管能力和发送能力是否失常、指定本人的初始化序列号为前面的可靠性传送做筹备。本质上就是连贯服务器指定端口,建设 TCP 连贯,并同步连贯单方的序列号和确认号,替换 TCP 窗口大小信息。当初,客户端处于 Closed
状态,服务端处于 Listen
状态
- 客户端给服务端收回一个连贯 SYN 报文,并指明
同步位 SYN=1,初始序号 seq=x
,而后客户端处于SYN_SEND
状态; - 服务器收到客户端的 SYN 报文后,会以本人的 SYN 报文
(SYN=1,ACK=1)
作为应答,并且指定本人的初始化序列号seq=y
,同时会把客户端的seq + 1 = x + 1
作为 ack 的值(ack=x+1)
,示意本人曾经收到客户端的SYN 报文
,此时服务器处于SYN_REVD
状态; - 客户端收到
SYN 报文
后,会发送一个 ACK 报文(ACK=1)
作为应答,并且之地当本人的序列号seq=x+1
,同时会把服务器的seq + 1 = y + 1
作为 ack 的值(ack=y+1)
,示意本人曾经收到服务器的SYN 报文
,此时客户端处于ESTABLISHED
状态,服务器收到 ACK 报文之后,也处于 ESTABLISHED 状态,此时,单方已建设起了连贯。
客户端发动申请,服务器解决申请
客服端会将申请行、申请头,申请体的相应信息发送给服务器。
服务器收到申请后进行逻辑解决,并依据处理结果返回响应数据(响应行、响应头、响应体等)。
这里提一下 HTTP 数据传输过程中的优化策略。在 HTTP 数据传输的过程中会将报文进行拆包(将报文拆分成小的数据包),顺次传输给接管方,接管方每次接管到数据包后必须向发送方确认,如果发送方没有收到这个确认的音讯,就断定为数据包失落,并从新发送该数据包,接管方须要实现组包(将每次接管的数据包按程序组装为残缺的数据包)从而取得残缺的数据包。
敞开 TCP 连贯
数据传输结束后还要依据 Connection
字段判断是否须要断开连接,若申请头或响应头中蕴含 Connection: Keep-Alive
示意长久连贯,之后申请同一站点的资源会复用此连贯;不满足上述情况就须要断开连接,断开连接遵循四次挥手准则。断开连接这个动作能够由客户端或者服务器任一方发动,此时,客户端和服务器都处于 ESTABLISHED
状态,假如由客户端发动敞开申请,则流程如下:
- 客户端发动一个连贯开释 FIN 报文段
(FIN=1)
,报文中指定一个序列号(seq=u)
,并进行发送数据,敞开 TCP 连贯。客户端进入FIN_WAIT1(终止期待 1)
状态,期待服务器的确认; - 服务器收到连贯开释的 FIN 报文段后,会收回 ACK 报文
(ACK=1)
,并指定本人的序列号(seq=v)
,同时会把客户端的seq + 1 = u + 1
作为 ack 的值(ack=u+1)
,表明曾经收到客户端的报文了。服务端进入CLOSE_WAIT(敞开期待)
状态,此时的 TCP 处于半敞开状态,客户端到服务端的连贯开释。客户端收到服务端的确认后,进入FIN_WAIT2(终止期待 2)
状态,期待服务端收回的连贯开释报文段; - 若此时服务器曾经没有数据须要发送给客户端了,服务器就会收回连贯开释报文段
(FIN=1, ACK=1, 序列号 seq=w, 确认号 ack=u+1)
,服务端进入LAST_ACK(最初确认)
状态,期待客户端的确认; - 客户端收到服务端的连贯开释报文后,对此收回确认报文段
(ACK=1, seq=u+1, ack=w+1)
,客户端进入TIME_WAIT(工夫期待)
状态。此时 TCP 未开释掉,须要通过工夫期待计时器设置的工夫2MSL
后,客户端才进入CLOSED
状态。
解决响应信息
网络过程接管到响应数据后开始解析响应头,解析到响应状态码会依据不同的码值进行解决,上面列举一下罕用的状态码:
200 OK
。申请解决胜利,响应数据放在响应体中。301 Moved Permanently
。永恒重定向,如果以前的域名地址不再应用,须要更换新的域名地址来拜访资源,能够将响应状态码置为 301,浏览器默认会做缓存优化,再次拜访原地址时会主动拜访重定向的那个地址302 Found
。长期重定向,若只是临时不应用原地址能够返回302
状态码。如:网站正在保护,那么能够在以后域给出一个解释页面来告诉访问者-
304 Not Modified
。协商缓存。浏览器首次申请资源时,服务器会将Last-Modified
和ETag
两个字段放在响应头中返回给浏览器。Last-Modified
。即资源的最初批改工夫,当浏览器第二次向服务器发动申请时,会在申请头中携带If-Modified-Since
字段,服务器拿到申请头中的If-Modified-Since
字段后会将字段值与以后服务器中该资源的Last-Modified
字段值比拟。若If-Modified-Since
的值小于服务器中Last-Modified
的值,表明服务器上的资源更新过,服务器会更新Last-Modified
的值并将新的资源返回给浏览器,响应状态码为200
;否则返回304
状态码,通知浏览器间接应用缓存资源;Etag
。即资源最初批改的内容,依据文件内容生成hash
值。当浏览器第二次向服务器发动申请时,会在申请头中携带If-None-Match
字段,服务器拿到申请头中的If-None-Match
字段后会将字段值与以后服务器中该资源的Etag
相比拟,若两值不相等,服务器将更新Etag
的值并返回新的资源给浏览器,响应状态码为200
;否则返回304
状态码,通知浏览器间接应用缓存资源。当Etag
与Last-Modified
同时存在时,先依据Etag
判断,再依据Last-Modified
判断返回什么状态码。
400 Bad Request
。申请参数有误401
。身份认证403 Forbidden
。服务器禁止拜访404 Not Found
。服务器未找到相应资源500 Internal Server Error
。服务器出错了503 Service Unavailable
。服务器忙碌,临时无奈解决响应服务
解析到响应数据类型会判断Content-Type
字段,它会通知浏览器服务器返回的响应体数据是什么类型,而后浏览器会依据它的值来决定如何显示响应体的内容。如果值是application/octet-stream
字节流类型,通常会按下载类型来解决;如果值是text/html
类型则筹备渲染过程。
筹备渲染过程
通常状况下关上一个 Tab 页就要启动一个渲染过程,但这里有个 同一站点(same-site)
的特例,属于同一站点的 Tab 页应用同一个渲染过程。同一站点的个性包含:
- 根域名加上协定雷同
-
属于同一个根域名下的所有子域名加上不同的端口号
// 同一站点 https://time.geekbang.org https://www.geekbang.org https://www.geekbang.org:8080
提交文档阶段
渲染过程筹备好后进入提交文档阶段。流程如下:
- 首先当浏览器过程接管到网络过程的响应头数据之后,便向渲染过程发动“提交文档”的音讯。
- 渲染过程接管到“提交文档”的音讯后,会和网络过程建设传输数据的“管道”。
- 等文档数据传输实现之后,渲染过程会返回“确认提交”的音讯给浏览器过程。
- 浏览器过程在收到“确认提交”的音讯后,会更新浏览器界面状态,包含了平安状态、地址栏的 URL、后退后退的历史状态,并更新 Web 页面。
构建 DOM 树
浏览器无奈间接了解和应用 HTML,所以须要将 HTML 转换为浏览器可能了解的 DOM 树结构。可在浏览器的控制台输出 document
进行查看。具体转换过程如下:
- 转换(字节 -> 字符):浏览器从磁盘或网络读取 HTML 的原始字节,并依据文件的指定编码(如:UTF-8)把它们转换成各个字符
- 令牌化(字符 -> 令牌):浏览器将字符转换为合乎 W3C 规范的令牌(如:
<html>
<body>
),以及其余尖括号内的字符串。每个令牌都具备非凡含意和一组规定 - 词法剖析(令牌 -> 节点):收回的令牌转换成定义其属性和规定的“对象”
- DOM 构建(节点 ->DOM):因为
html
标记定义不同标记之间的关系,创立的对象链接在一个树数据结构内,此构造也会捕捉原始标记中定义的父项 - 子项关系:HTML
对象是body
对象的父项,body
是paragraph
对象的父项,依此类推。
在解析 HTML 文件的过程中,可能中途须要网络过程去下载脚本文件以及款式文件,那么就有一个阻塞 DOM 解析以及渲染的问题,具体可参考 原来 CSS 与 JS 是这样阻塞 DOM 解析和渲染的 这篇文章,把论断贴一下:
- CSS 不会阻塞 DOM 的解析,但会阻塞 DOM 渲染。
- JS 阻塞 DOM 解析,但浏览器会 ” 偷看 ”DOM,事后下载相干资源。
-
浏览器遇到
<script>
且没有defer
或async
属性的标签时,会触发页面渲染,如果后面 CSS 资源尚未加载结束,浏览器就会期待它加载结束再执行脚本。款式计算
把 CSS 转换为浏览器可能了解的构造:浏览器无奈间接了解 CSS 款式,所以当渲染引擎接管到 CSS 文本时,会执行转换操作,将 CSS 文本转换为 styleSheets。可在浏览器的控制台输出
document.styleSheets
进行查看。具体转换过程如下: - 转换样式表中的属性值,使其标准化:比方,在编写代码时应用的是十六进制的色彩,须要转换成 rgb 的格局,李兵老师的课程有说把 em 单位转为 px 单位,bold 转为 700,我关上 Chrome 的开发者工具看并没有转,这里跟老师的形容有点出入。
- 计算出 DOM 树中节点的款式:次要通过继承规定和层叠规定来进行计算。计算实现之后输入每个 DOM 节点的款式,并保留到 ComputedStyle 的构造内。
- 继承规定:子节点如果没有设置
font-size
、color
、font-family
几个属性的款式,那么能够继承父节点的款式,如果父节点也没有设置其款式,那么默认应用 UserAgent 款式。留神:只有可继承属性能力继承。 - 层叠规定:它是一个定义了如何合并来自多个源的属性值的算法。
布局阶段
计算出 DOM 树中可见元素的几何地位。具体过程如下:
- 创立布局树:遍历 DOM 树中所有可见节点,生成一棵只蕴含可见节点的布局树。不可见的节点会被疏忽,不会呈现在布局树上,不可见节点包含:1. 不会渲染输入的节点(script/link/meta/head),2. 通过 CSS 暗藏的节点(display: none)会被疏忽掉;
- 布局计算:计算布局树节点的几何地位,并将计算出的信息保留到布局树中。
分层
因为页面中有很多简单的成果,如一些简单的 3D 变换、页面滚动,或者应用 z-index 做 z 轴排序等,为了更加不便地实现这些成果,渲染引擎还须要为特定的节点生成专用的图层,并生成一棵对应的图层树(LayerTree)。通常状况下,并不是布局树中每个节点都蕴含一个图层,如果一个节点没有对应的层,那么这个节点就从属于父节点的图层。这些图层通过合成变为最终的页面。渲染引擎为特定节点创立独自的图层的条件包含:
- 领有层叠上下文属性的元素会被晋升为独自的一层。以下是层叠上下文属性示意图:
- 须要剪裁(clip)的中央也会被创立为图层。当一个元素设置了固定宽高而外面的内容超出了这个元素,那么超出的局部会被剪裁,渲染引擎会创立一个独自的图层来寄存被剪裁的内容。
图层绘制
构建完图层树之后,渲染引擎会对图层树中的每个图层进行绘制。渲染引擎会把一个图层的绘制拆分成很多小的绘制指令,而后再把这些指令依照程序组成一个待绘制列表。
栅格化(raster)
绘制列表筹备好之后,主线程会把绘制列表提交给合成线程,由合成线程来实现具体的绘制操作。先理解一下什么是视口(viewport)?视口就是屏幕上页面的可见区域。随着业务的复杂性,某些状况下图层可能会很长,而用户通过视口只能看见页面的一小部分,如果一次性绘制图层会产生很大的开销,所以合成线程会将图层划分为图块(tile),图块大小通常是 256x256
或者 512x512
。
合成线程会依照视口左近的图块来优先生成位图,理论生成位图的操作是由栅格化来执行的。所谓栅格化,是指将图块转换为位图。栅格化过程都会应用 GPU 来减速生成,应用 GPU 生成位图的过程叫疾速栅格化,生成的位图被保留在 GPU 内存中。
合成与显示
所有的图层进行光栅化之后,合成线程就会生成一个绘制图块的 DrawQuad
命令,合成线程将这个命令发给浏览器过程,由浏览器过程中的 viz
组件接管 DrawQuad
命令,它会依据命令将页面内容绘制到内存中,最初将内存中的内容显示到屏幕上。
参考文章
- 浏览器工作原理与实际
- 实际这一次, 彻底搞懂浏览器缓存机制