乐趣区

关于浏览器:从输入URL到页面呈现超详细

解析地址栏中的信息

浏览器监听用户输出的信息并尝试匹配你想要拜访的网址或关键词。以掘金为例,在浏览器地址栏中输出信息,而后回车,浏览器会进行以下判断:

  • 判断是否是非法的 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 cachedisk 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-ModifiedETag 两个字段放在响应头中返回给浏览器。

    • 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 状态码,通知浏览器间接应用缓存资源。当 EtagLast-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 对象的父项,bodyparagraph 对象的父项,依此类推。

在解析 HTML 文件的过程中,可能中途须要网络过程去下载脚本文件以及款式文件,那么就有一个阻塞 DOM 解析以及渲染的问题,具体可参考 原来 CSS 与 JS 是这样阻塞 DOM 解析和渲染的 这篇文章,把论断贴一下:

  • CSS 不会阻塞 DOM 的解析,但会阻塞 DOM 渲染。
  • JS 阻塞 DOM 解析,但浏览器会 ” 偷看 ”DOM,事后下载相干资源。
  • 浏览器遇到 <script> 且没有 deferasync 属性的标签时,会触发页面渲染,如果后面 CSS 资源尚未加载结束,浏览器就会期待它加载结束再执行脚本。

    款式计算

    把 CSS 转换为浏览器可能了解的构造:浏览器无奈间接了解 CSS 款式,所以当渲染引擎接管到 CSS 文本时,会执行转换操作,将 CSS 文本转换为 styleSheets。可在浏览器的控制台输出 document.styleSheets 进行查看。具体转换过程如下:

  • 转换样式表中的属性值,使其标准化:比方,在编写代码时应用的是十六进制的色彩,须要转换成 rgb 的格局,李兵老师的课程有说把 em 单位转为 px 单位,bold 转为 700,我关上 Chrome 的开发者工具看并没有转,这里跟老师的形容有点出入。
  • 计算出 DOM 树中节点的款式:次要通过继承规定和层叠规定来进行计算。计算实现之后输入每个 DOM 节点的款式,并保留到 ComputedStyle 的构造内。
  • 继承规定:子节点如果没有设置 font-sizecolorfont-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 命令,它会依据命令将页面内容绘制到内存中,最初将内存中的内容显示到屏幕上。

参考文章

  • 浏览器工作原理与实际
  • 实际这一次, 彻底搞懂浏览器缓存机制
退出移动版