从浏览器地址栏输入url到显示页面的步骤

1,909 阅读18分钟

==从浏览器地址栏输入url到显示页面的步骤==

目录

1.概述

2.DNS域名解析

3.三次握手与四次挥手

4.DOM,CSSOM,渲染树的构建、渲染与绘制

1.概述

浏览器根据请求的 URL 交给 ==DNS 域名解析==,找到真实 IP ,向服务器发起请求;

==三次握手==建立连接,服务器交给后台处理完成后返回数据,浏览器接收⽂件( HTML、JS、CSS 、图象等),数据传输完成后==四次挥手==断开连接;

浏览器对加载到的资源( HTML、JS、CSS 等)进⾏==语法解析==,建⽴相应的内部数据结构

(如 HTML 的 DOM );

载⼊解析到的资源⽂件,==渲染⻚⾯==,完成。

第一步: 当你在浏览器地址栏输入一个网站地址并按下回车键时,浏览器会构建请求行,请求行结构很简单,由请求方法、请求URI和HTTP版本协议组成。例如:

GET / HTTP/1.1 // 请求方法是GET,路径为根路径,HTTP协议版本为1.1

查找强缓存

2. DNS(域名解析)

浏览器获取主机ip地址,过程如下:

说明:DNS域名解析本质上==就是通过地址栏寻找服务器IP地址的过程==

举例:www.baidu.com (域名) - DNS解析 -> 182.61.200.6 (IP地址)

2.1第一大步:先查缓存

2.1.1浏览器缓存

当用户通过浏览器访问域名时,浏览器首先会在即的缓存中查找是否有该域名对应的IP地址

(chrome://version)

image.png

image.png

==强缓存==(www.cnblogs.com/chengxs/p/1…

强缓存通过 ==expires== 和 ==cache-control==控制 协商缓存(为什么有expires 了,还需要cache-control? 答:因为expires 有个服务器和浏览器时间可能存在不同步的问题,expires是绝对时间 cache-control是相对时间)

通过==Last-Modify==和==ETag==进行控制

Last-Modify有精度问题, 精确到到秒;

而ETag(可以保证每一个资源都是唯一 的,资源变化都会导致ETag变化,ETag值变更则说明资源已经被修改) ,没有精度问题 只要文件改变ETag值就改 变

==协商缓存==

协商缓存在协商什么?

浏览器问服务器我缓存的文件有没有更新啊?

1.没有更新 浏览器可以用缓存,重定向状态码304( Not Modified ⾃从上次请求后,请求的⽹⻚未修改过。)

2.文件更新 浏览器不能用缓存服务器发新的给浏览器,成功状态码200( OK 正常返回信息)

==注意==:

(强缓存:浏览器直接从本地缓存中获取数据,不与服务器进行交互;

协商缓存:浏览器发送请求到服务器,服务器判断是否可使用本地缓存)

第一次请求

image.png

第二次请求

preview

​ ==缓存方式==?

​ (1)内存缓存(from memory cache):内存缓存具有两个特点,分别是快速读取时效性

​ 1、快速读取:内存缓存会将编译解析后的文件,直接存入该进程的内存中,占据该进程一定的内存资源,以方便下次运行使用时的快速读取。

​ 2、时效性:一旦该进程关闭,则该进程的内存则会清空。

img

​ (2)硬盘缓存(from disk cache):硬盘缓存则是直接将缓存写入硬盘文件中,读取缓存需要对该缓存存放的硬盘文件 进行I/O操作,然后重新解析该缓存内容,读取复杂,速度比内存缓存慢。

img

2)==系统缓存==。当浏览器缓存中无域名对应IP则会自动检查用户计算机系统Hosts文件DNS缓存是否有该域名对应的

IP(C:\Windows\System32\drivers\etc)

image.png

3)==路由器缓存==。当浏览器及系统缓存中均无域名对应IP则进入路由器缓存中检查,以上三步均为客服端的DNS缓存

img

4)DNS服务器缓存/ ==ISP(互联网服务提供商)缓存==。(移动、联通、电信、珠江宽频、长城宽带…等)

img

image.png

2.2第二大步:DNS服务器查找

www.baidu.com --> www.baidu.com.)

5)==根域名解析==。 当以上均未完成,则进入根服务器进行查询。全球仅有13台根域名服务器,1个主根域名服务器,其余12为辅根域名服务器。根域名收到请求后会查看区域文件记录,若无则将其管辖范围内顶级域名(如.com)服务器IP告诉本地DNS服务器 (利用UDP协议,UDP最大只有512字节)

什么域名解析用UDP协议?

因为UDP快!UDP的DNS协议只要一个请求、一个应答就好了。而使用基于TCP的DNS协议要三次握手、发送数据以及应答、四次挥手。但是UDP协议传输内容不能超过512字节。不过客户端向DNS服务器查询域名,一般返回的内容都不超过512字节,用UDP传输即可。

6)==顶级域名解析==。(比如:com , net , gov , cn , us , uk......) 顶级域名服务器收到请求后查看区域文件记录,若无则将其管辖范围内主域名服务器的IP地址告诉本地DNS服务器

7)==权限域名解析==. 主域名服务器接受到请求后查询自己的缓存,如果没有则进入下一级域名服务器进行查找,并重复该步骤直至找到正确纪录

​ i)二级域名解析。( 顶级域名cn下面有:hk , bj , edu , gov.... )

​ ii)三级域名解析。(二级域名edu下面有:tsinghua , fudan , pku......)

​ iii)三级域名解析。(三级域名tsinghua下面有lib , jwc , mailreg)

​ ....

image.png

DNS服务器查找往往采用:==递归查询==或==迭代查询==

image.png

8)保存结果到缓存。

3.三握四挥:

image.png

TCP报头中的==源端口号==和==目的端口号==同IP数据报中的==源IP==与==目的IP==唯一确定一条TCP连接

seq 32位序号(4字节=32位): 37 59 56 75 ; 用来标识TCP发端向TCP收端发送的数据字节流

ack 确认序号(4字节=32位):

由于该报文为SYN报文,ACK标志为0,故没有确认序号(ACK标志为1时确认序号才有效)TCP协议规定,只有ACK=1时有效,也规定连接建立后所有发送的报文的ACK必须为1

一旦连接建立,该值将始终发送(同ACK标志)

头部长度:该字段占用4位,用来表示报文首部的长度,单位是4Byte。如:headLen = ((packet[12]>>4)&0x0F)*4;

预留6位:长度为6位,作为保留字段,暂时没有什么用处。

URG:长1位,表示紧急指针字段有效;

==ACK==:长1位,置位表示确认号字段有效;TCP协议规定,==只有ACK=1==时有效,也规定连接建立后所有发送的报文的ACK必须为1

PSH:长1位,表示当前报文需要请求推(push)操作;

RST:长1位,置位表示复位TCP连接;

==SYN==:长1位,在连接建立时用来同步序号。当SYN=1而ACK=0时,表明这是一个连接请求报文。对方若同意建立连接,则应在响应报文中使SYN=1和ACK=1. 因此,==SYN置1就表示这是一个连接请求或连接接受报文==。

==FIN==:长1位,用于释放TCP连接时标识发送方比特流结束;即完,终结的意思, 用来释放一个连接。==当 FIN = 1时,表明此报文段的发送方的数据已经发送完毕,并要求释放连接==。

窗口大小:长度为16位,2个字节。

校验和:长度为16位,2个字节。

紧急指针:长度为16位,2个字节。

以上是TCP包头必须要有的字段,也称固有字段,长度为20个字节。

3.1三次握手建立连接

image.png

image.png

问:==为什么不是两次握手,而是三次握手?==

答:因为两次握手的话,客户端知道了服务端可以收发报文,但是服务端只知道客户端可以发送报文,但不知道客户端是否可以接受报文。

TCP 协议是传输层的一个面向连接的安全可靠的一个传输协议,三次握手的机制是为了保证能建立一个安全可靠的连接,那么第一次握手是由客户端发起,客户端会向服务端发送一个报文,在报文里面:SYN标志位置为1,表示发起新的连接。当服务端收到这个报文之后就知道客户端要和我建立一个新的连接,于是服务端就向客户端发送一个确认消息包,在这个消息包里面:ack标志位置为1,表示确认客户端发起的第一次连接请求。以上两次握手之后,对于客户端而言:已经明确了我既能给服务端成功发消息,也能成功收到服务端的响应。但是对于服务端而言:两次握手是不够的,因为到目前为止,服务端只知道一件事,客户端发给我的消息我能收到,但是我响应给客户端的消息,客户端能不能收到我是不知道的。所以,还需要进行第三次握手,第三次握手就是当客户端收到服务端发送的确认响应报文之后,还要继续去给服务端进行回应,也是一个ack标志位置1的确认消息。通过以上三次连接,不管是客户端还是服务端,都知道我既能给对方发送消息,也能收到对方的响应。那么,这个连接就被安全的建了。

过度步骤:==服务器向客户端传输数据==

传输完毕后需要断开连接....... 那么如何断开连接呢?

3.2四次挥手

image.png

问:==客户端有必要最后设置2MSL的等待时间吗?==

答:如果没有2MSL的等待时间,那么如果客户端最后一次确认报文丢失,那么服务器端就会因为迟迟收不到客户端的确认报文而不断触发服务端的超时重发,如下图所示:

image.png

问:==服务器端的两次报文为什么不合并为一个报文一次发送?==

答:因为客户端发送Fin=1的断开连接请求报文,服务器端可能还有未传输完成的剩余数据,所以服务器端在收到客户端的断开请求时,首先对客户端的断开请求报文做回应,发送ACK=1的确认收到,表示:“ 你发的FIN=1报文我收到了 (但是并不意味这数据传输完毕)”,等待服务器端数据全部传输完毕后,服务器端返回Fin=1的释放连接报文。

过度步骤:==如果资源可缓存,则进行缓存;并对响应进行解码(例如gzip压缩),接下来根据资源类型决定如何处理==

4.浏览器解析,渲染

掘金-你不知道的浏览器页面渲染机制(juejin.cn/post/684490…

服务端接收到http请求后,经过计算(向不同的用户推送不同的内容) ,返回 HTTP 请求,返回的内容如下:

img

其实就是一堆 HMTL 格式的字节,因为只有 HTML 格式浏览器才能正确解析,这是 W3C 标准的要求。接下来就是浏览器的渲染过程。

4.0浏览器渲染过程(概述)

1)浏览器会解析三个东西:

  • 一是HTML/SVG/XHTML,HTML字符串描述了一个页面的结构,浏览器会把HTML结构字符串解析转换==DOM树==形结构。

image.png

  • 二是CSS,解析CSS会产生CSS规则树,它和DOM结构比较像。

img

  • 三是Javascript脚本,等到Javascript 脚本文件加载后, 通过 DOM API 和 CSSOM API 来操作 DOM Tree 和 CSS Rule Tree。

image.png

2)解析完成后,浏览器引擎会通过DOM Tree 和 CSS Rule Tree 来构造 Rendering Tree。

  • Rendering Tree 渲染树并不等同于DOM树,渲染树只会包括需要显示的节点和这些节点的样式信息。
  • CSS 的 Rule Tree主要是为了完成匹配并把CSS Rule附加上Rendering Tree上的每个Element(也就是每个Frame)。
  • 然后,计算每个Frame 的位置,这又叫layout和reflow过程。

3)最后通过调用操作系统Native GUI的API绘制。

( 接下来我们针对这其中所经历的重要步骤详细阐述 )

4.1构建DOM

浏览器会遵守一套步骤将HTML 文件转换为 DOM 树。宏观上,可以分为几个步骤:

构建DOM的具体步骤

  • 浏览器从磁盘或网络读取HTML的==原始字节==,并根据文件的指定编码(例如 UTF-8)将它们转换成==字符串==。

在网络中传输的内容其实都是 0 和 1 这些字节数据。当浏览器接收到这些字节数据以后,它会将这些字节数据转换为字符串,也就是我们写的代码。

  • 将字符串转换成==Token==,例如:等。Token中会标识出当前Token是“开始标签”或是“结束标签”亦或是“文本”等信息

这时候你一定会有疑问,节点与节点之间的关系如何维护?

事实上,这就是Token要标识“起始标签”和“结束标签”等标识的作用。例如“title”Token的起始标签和结束标签之间的==节点==肯定是属于“head”的子节点。

img

上图给出了节点之间的关系,例如:“Hello”Token位于“title”开始标签与“title”结束标签之间,表明“Hello”Token是“title”Token的子节点。同理“title”Token是“head”Token的子节点。

  • 生成节点对象并构建==DOM==

事实上,==构建DOM的过程中,不是等所有Token都转换完成后再去生成节点对象,而是一边生成Token一边消耗Token来生成节点对象==。换句话说,每个Token被生成后,会立刻消耗这个Token创建出节点对象。注意:带有结束标签标识的Token不会创建节点对象。

接下来我们举个例子,假设有段HTML文本:

<html>

Web page parsing

<body> <div> <h1>Web page parsing</h1> <p>This is an example Web page.</p> </div> </body> </html>

上面这段HTML会解析成这样:

img

4.2构建CSSOM

DOM会捕获页面的内容,但浏览器还需要知道页面如何展示,所以需要构建CSSOM。

构建CSSOM的过程与构建DOM的过程非常相似,当浏览器接收到一段CSS,浏览器首先要做的是识别出Token,然后构建节点并生成CSSOM

img

在这一过程中,浏览器会确定下每一个节点的样式到底是什么,并且这一过程其实是很消耗资源的。因为样式你可以自行设置给某个节点,也可以通过继承获得。在这一过程中,浏览器得递归 ==CSSOM 树==,然后确定具体的元素到底是什么样式。

注意:CSS匹配HTML元素是一个相当复杂和有性能问题的事情。所以,DOM树要小,CSS尽量用id和class,千万不要过渡层叠下去

4.3构建渲染树

当我们生成 DOM 树和 CSSOM 树以后,就需要将这两棵树组合为==渲染树==。

img

在这一过程中,不是简单的将两者合并就行了。渲染树只会包括需要显示的节点和这些节点的样式信息,如果某个节点是 display: none 的,那么就不会在渲染树中显示。

我们或许有个疑惑:==浏览器如果渲染过程中遇到JS文件怎么处理==?

渲染过程中,如果遇到``就停止渲染,执行 JS 代码。因为浏览器有GUI渲染线程与JS引擎线程,为了防止渲染出现不可预期的结果,这两个线程是互斥的关系。 JavaScript的加载、解析与执行会阻塞DOM的构建,也就是说,在构建DOM时,HTML解析器若遇到了JavaScript,那么它会暂停构建DOM,将控制权移交给JavaScript引擎,等JavaScript引擎运行完毕,浏览器再从中断的地方恢复DOM构建。

也就是说,如果你想首屏渲染的越快,就越不应该在首屏就加载 JS 文件,这也是都建议将 script 标签放在 body 标签底部的原因。当然在当下,并不是说 script 标签必须放在底部,因为你可以给 script 标签添加 defer 或者 async 属性(下文会介绍这两者的区别)。

JS文件不只是阻塞DOM的构建,它会导致CSSOM也阻塞DOM的构建

原本DOM和CSSOM的构建是互不影响,井水不犯河水,但是一旦引入了JavaScript,CSSOM也开始阻塞DOM的构建,只有CSSOM构建完毕后,DOM再恢复DOM构建。

这是什么情况?

这是因为JavaScript不只是可以改DOM,它还可以更改样式,也就是它可以更改CSSOM。因为不完整的CSSOM是无法使用的,如果JavaScript想访问CSSOM并更改它,那么在执行JavaScript时,必须要能拿到完整的CSSOM。所以就导致了一个现象,如果浏览器尚未完成CSSOM的下载和构建,而我们却想在此时运行脚本,那么浏览器将延迟脚本执行和DOM构建,直至其完成CSSOM的下载和构建。也就是说,==在这种情况下,浏览器会先下载和构建CSSOM,然后再执行JavaScript,最后在继续构建DOM。== img

4.4布局与绘制

当浏览器生成渲染树以后,就会根据渲染树来进行布局(也可以叫做回流)。这一阶段浏览器要做的事情是要弄清楚各个节点在页面中的确切位置和大小。通常这一行为也被称为“自动重排”。

布局流程的输出是一个“盒模型”,它会精确地捕获每个元素在视口内的确切位置和尺寸,所有相对测量值都将转换为屏幕上的绝对像素。

布局完成后,浏览器会立即发出“Paint Setup”和“Paint”事件,将渲染树转换成屏幕上的像素。

4.5重绘和重排(扩展)

渲染的流程基本上是这样(如下图黄色的四个步骤):1.计算CSS样式 2.构建Render Tree 3.Layout – 定位坐标和大小 4.正式开画

img

这里重要要说两个概念,一个是Reflow,另一个是Repaint

  • ==重绘==:当我们对 DOM 的修改导致了==样式的变化、却并未影响其几何属性(比如修改了颜色或背景色)时==,浏览器不需重新计算元素的几何属性、直接为该元素绘制新的样式(跳过了上图所示的回流环节)。
  • ==回流==:当我们对 DOM 的修改引发==了 DOM 几何尺寸的变化(比如修改元素的宽、高或隐藏元素等)时==,浏览器需要重新计算元素的几何属性(其他元素的几何属性和位置也会因此受到影响),然后再将计算的结果绘制出来。这个过程就是回流(也叫重排)

我们知道,当网页生成的时候,至少会渲染一次。在用户访问的过程中,还会不断重新渲染。重新渲染会重复回流+重绘或者只有重绘。 ==重排(回流)会发生重绘,重绘不一定会引发重排(回流)==。重绘和回流会在我们设置节点样式时频繁出现,同时也会很大程度上影响性能。回流所需的成本比重绘高的多,改变父节点里的子节点很可能会导致父节点的一系列回流。

1)常见引起重排(回流)属性和方法

任何会==改变元素几何信息(元素的位置和尺寸大小)==的操作,都会触发回流,

  • 添加或者删除可见的DOM元素;
  • 元素尺寸改变——边距、填充、边框、宽度和高度
  • 内容变化,比如用户在input框中输入文字
  • 浏览器窗口尺寸改变——resize事件发生时
  • 计算 offsetWidth 和 offsetHeight 属性
  • 设置 style 属性的值

2)常见引起重绘属性和方法 img

3)如何减少回流、重绘

  • 使用 transform 替代 top

  • 使用 visibility 替换 display: none ,因为前者只会引起重绘,后者会引发回流(改变了布局)

  • 不要把节点的属性值放在一个循环里当成循环里的变量。

    for(let i = 0; i < 1000; i++) { // 获取 offsetTop 会导致回流,因为需要去获取正确的值 console.log(document.querySelector('.test').style.offsetTop) }

不要使用 table 布局,可能很小的一个小改动会造成整个 table 的重新布局

动画实现的速度的选择,动画速度越快,回流次数越多,也可以选择使用 requestAnimationFrame

CSS 选择符从右往左匹配查找,避免节点层级过多

将频繁重绘或者回流的节点设置为图层,图层能够阻止该节点的渲染行为影响别的节点。比如对于 video 标签来说,浏览器会自动将该节点变为图层。

4.6总结

综上所述,我们得出这样的结论:

  • 浏览器工作流程:构建DOM -> 构建CSSOM -> 构建渲染树 -> 布局 -> 绘制。
  • CSSOM会阻塞渲染,只有当CSSOM构建完毕后才会进入下一个阶段构建渲染树。
  • 通常情况下DOM和CSSOM是并行构建的,但是当浏览器遇到一个不带defer或async属性的script标签时,DOM构建将暂停,如果此时又恰巧浏览器尚未完成CSSOM的下载和构建,由于JavaScript可以修改CSSOM,所以需要等CSSOM构建完毕后再执行JS,最后才重新DOM构建。

(注:本文为个人学习整理,文中如有不妥之处,还望评论区指出!)