从输入一个网址到浏览器显示页面经历的过程


一.浏览器根据域名解析IP地址

浏览器根据访问的域名找到其IP地址。DNS查找过程如下:

1.浏览器缓存

浏览器会缓存DNS记录一段时间。 有趣的是,操作系统没有告诉浏览器储存DNS记录的时间,这样不同浏览器会储存个自固定的一个时间(2分钟到30分钟不等)。看自身的缓存中是否是有域名对应的条目。如果有且没有过期则解析到此结束。

2.系统缓存

如果在浏览器缓存里没有找到需要的记录,浏览器会做一个系统调用(windows里是gethostbyname)。这样便可获得系统缓存DNS中的记录。如果有且没有过期则解析到此结束。

3.路由器缓存

如果系统缓存也没有找到,则会向路由器发送查询请求。它一般会有自己的DNS缓存。

4.ISP(互联网服务提供商) DNS 缓存

如果在路由缓存也没找到,最后要查的就是ISP缓存DNS的服务器。在这一般都能找到相应的缓存记录。

递归搜索
你的ISP的DNS服务器从跟域名服务器开始进行递归搜索,从.com顶级域名服务器到Facebook的域名服务器。一般DNS服务器的缓存中会有.com域名服务器中的域名,所以到顶级服务器的匹配过程不是那么必要了。

域名服务器向客户端返回查询结果域名,从而完成域名到IP地址的转换

二.建立TCP连接

客户端浏览器与web服务器建立TCP安全连接(三次握手),为之后的HTTP响应做准备。

TCP三次握手连接:

  • 第一次握手:客户端发送syn包(syn=j)到服务器,并进入 SYN_SEND 状态,等待服务器确认;

  • 第二次握手:服务器收到syn包,必须确认客户的 SYN(ack=j+1),同时自己也发送一个SYN包(syn=k),即 SYN+ACK包,此时服务器进入 SYN_RECV 状态;

  • 第三次握手:客户端收到服务器的 SYN+ACK 包,向服务器发送确认包ACK(ack=k+1),此包发送完毕,客户端和服务器进入 ESTABLISHED 状态,完成三次握手。

握手过程中传送的包里不包含数据,三次握手完毕后,客户端与服务器才正式开始传送数据。理想状态下,TCP连接一旦建立,在通信双方中的任何一方主动关闭连接之前,TCP 连接都将被一直保持下去。

三.客户端向web服务器发送HTTP请求

客户端(浏览器)发送HTTP请求,服务端(服务器)响应请求

如果浏览器存储了该域名下的cookie,那么cookie也会放入HTTP请求中
HTTP请求是一个基于TCP协议之上的应用层协议——超文本传输协议。浏览器通过DNS获取到web服务器真的IP地址后,便向Web服务器发起TCP连接请求,通过TCP三次握手建立好连接后,浏览器便可以将HTTP请求数据通过发送给服务器了。

四.处理HTTP请求并响应

服务器在收到浏览器发送的HTTP请求之后,会将收到的HTTP报文封装成HTTP的Request对象,并通过不同的Web服务器进行处理,处理完的结果以HTTP的Response对象返回,主要包括状态码,响应头,响应报文三个部分。

状态码主要包括以下部分:

  • 1xx:指示信息–表示请求已接收,继续处理。
  • 2xx:成功–表示请求已被成功接收、理解、接受。
  • 3xx:重定向–要完成请求必须进行更进一步的操作。
  • 4xx:客户端错误–请求有语法错误或请求无法实现。
  • 5xx:服务器端错误–服务器未能实现合法的请求。

响应头主要由Cache-Control、 Connection、Date、Pragma等组成;

响应体为服务器返回给浏览器的信息,主要由HTML,css,js,图片文件组成。

五.页面渲染

浏览器加载,解析,渲染页面,分为五个步骤:

* 浏览器将获取的 HTML 文档解析成 DOM 树;
* 处理 CSS 标记,构成层叠样式表模型(CSSOM);
* 将 DOM 和 CSSOM 合并为渲染树(render tree);
* 渲染树的每个元素的内容(如:节点的几何信息(位置,大小))都是计算过的,称之为 布局layout;
* 将渲染树上的各个节点绘制到屏幕上,称之为 绘制painting;

解析html -> 构建dom树 -> 构建render树 -> 布局render树 -> 绘制render树

1、构建 DOM 树

浏览器从服务器拿到 HTML 文档后,从上至下开始解析 HTML ,遍历所有的节点生成 DOM 树(Document Object Model 文档对象模型);

DOM 树 的构建过程是一个深度遍历过程,当前节点的所有子节点都构建好后才会去构建当前节点的下一个兄弟节点;

1. DOM 树的构建可能会被 CSS 和 JS 的加载而阻塞;如:遇到 CSS ,会开启线程下载 CSS ;
2. display: none 的元素也会在 DOM 树中;
3. 注释也会在 DOM 树中;
4. script 标签也会在 DOM 树中;

2、解析 CSS

浏览器会解析 CSS 文件并生成 CSS 规则树(或CSSOM树,CSS Object Model CSS对象模型) ,每个 CSS 文件都会被解析成 StyleSheet 对象,每个对象都包括 CSS 规则,CSS规则对象包括对应的选择器和声明对象以及其他对象;

1. CSS 的解析可以与 DOM 的解析同步进行
2. CSS 的解析与 script 的执行互斥
3. 在 Webkit 内核中进行了 script 执行优化,只有在 JS 访问 CSS 时才会发生互斥 

3、构建渲染树

根据 DOM 树和 CSS 规则树,浏览器就可以构建渲染render 树了,渲染树不等于DOM树。浏览器首先遍历 DOM 树上的每一个可见节点,然后对每个可见节点找到匹配的 CSS 样式规则并应用;

1. 渲染树和 DOM 树不完全对对应;DOM树表示页面结构,渲染树表示DOM节点如何显示;
2. display: none 的元素不在渲染树中;
3. visiblity: none 的元素在渲染树中;

4、渲染树布局

布局阶段浏览器会遍历渲染树的所有节点,由于每个节点都是一个render对象,都包含各节点的CSS的宽高、位置、背景等样式信息,所以浏览器就能根据这写信息可以计算出每个节点在屏幕中的位置,及其样式;

1. float 元素、absolute 元素、fixed 元素会发生位置偏移;
2. 脱离标准文档流就是脱离渲染树(render tree);

5、渲染树的绘制

绘制阶段,浏览器遍历所有的渲染 render 树节点,调用 paint() 方法,将其渲染在屏幕上。渲染树的绘制工作是由浏览器的UI后端组件完成的。

6、浏览器渲染过程可能出现的事情

6-1.阻塞渲染

浏览器会延迟 JavaScript 的执行和 DOM 的构建

1. CSS 被默认为阻塞渲染的资源,所以在 CSSOM 构建完成之前不会处理任何已处理的内容;
2. JavaScript 可以读取和修改 DOM 属性,还可读取和修改 CSSOM 属性,所以 CSS 解析和 script 的执行互斥

css注意事项

1.dom深度尽量浅;
2.减少inline javascript、css的数量;
3.避免使用通配符。

JavaScript 注意事项

1.如果在解析html的时候遇到js会阻塞页面渲染,所以一般将script标签放到页面底部,也就是body闭合标签之前,这能确保在脚本执行前页面已经完成了DOM树渲染。
2.尽可能地合并脚本。页面中的script标签越少,加载也就越快,响应也越迅速。无论是外链脚本还是内嵌脚本都是如此。
3.采用无阻塞下载 JavaScript 脚本的方法:
(1)使用script标签的 defer、async 属性;
(2)使用动态创建的script元素来下载并执行代码等异步加载等方法;

**defer、async 区别:**
defer、async都是异步下载,但是执行时刻不一致;
相同点:
1.加载文件时不阻塞页面渲染;
2.使用这两个属性的脚本中不能调用document.write方法;
3.允许不定义属性值,仅仅使用属性名;
不同点:
1.html的版本html4.0中定义了defer,html5.0中定义了async;这将造成由于浏览器版本的不同而对其支持的程度不同;
2.每一个async属性的脚本都在它下载结束之后立刻执行,同时会在window的load事件之前执行,所以就有可能出现脚本执行顺序被打乱 的情况;
3.每一个defer属性的脚本都是在页面解析完毕之后,按照原本的顺序执行,同时会在document的DOMContentLoaded之前执行;

因此,script标签的位置很重要我们在实际开发中应该尽量坚持以下两个原则:

1. 在引入顺序上,CSS 资源先于 JavaScript 资源;
2. JavaScript 尽量少去影响 DOM 的构建;

6-2.回流和重绘( Reflow 和 Repaint)

Reflow(回流/重排):当浏览器发现某个部分发生了变化影响了布局,这个时候渲染树需要重新计算。

Repaint(重绘):当改变某个元素的背景色、字体颜色、边框颜色等,不影响元素周围或内部布局的属性时,将只会引起浏览器的 Repaint,根据元素的新属性重新绘制,使元素呈现新的外观,但是元素的几何尺寸和位置都没有发现变化。重绘不会带来重新布局,并不一定伴随重排;

也就是说:“重绘”不一定会出现”重排”,”重排”必然会出现”重绘”

Reflow 要比 Repaint 更花费时间,也就更影响性能。所以在写代码的时候,要尽量避免过多的 Reflow。

Tips:display:none 会触发 reflow;visibility: hidden 不会出发 reflow,只会触发 repaint;

可以通过一些操作来减少 Reflow:

1. 用transform 来做形变和位移;
2. 通过绝对定位的方式来脱离当前层叠上下文,形成新的 render layout
3. 异步 reflow;

发生Reflow 的原因:

  1. 页面初始化的时候;
  2. 操作DOM时,如添加或者删除可见的DOM元素;
  3. 元素位置改变;
  4. 元素的尺寸改变——边距、填充、边框、宽度和高度;
  5. 元素内容改变;(例如:一个文本被另一个不同尺寸的图片替代,用户在input框中输入文字)
  6. 浏览器窗口尺寸改变——resize事件发生时;
  7. 计算 offsetWidth 和 offsetHeight 属性;

减少 Reflow/Repaint

  • 不要一条一条地修改 DOM 的样式。与其这样,还不如预先定义好 css 的 class,然后修改 DOM 的 className。
  • 不要把 DOM 结点的属性值放在一个循环里当成循环里的变量。
  • 为动画的 HTML 元件使用 fixed 或 absoult 的 position,那么修改他们的 CSS 是不会 Reflow的。
  • 千万不要使用 table 布局。因为可能很小的一个小改动会造成整个 table 的重新布局。

6-3.优化渲染效率建议

1. 合法地去书写 HTML 和 CSS ,且不要忘了文档编码类型;
2. 样式文档应该在 head 标签中,而脚本文档应该在 body 结束之前,防阻塞;
3. 优化 CSS 选择器,尽量少嵌套;
4. 减少 JavaScript 对 DOM 的操作;
5. 修改元素的样式时,修改 class 是性能最高的方法;
6. 尽量用 transform 来做形变和位移。

六.关闭连接

通过四次挥手,断开连接。

最后来一个加载渲染的过程:

1.用户输入网址(假设是第一次访问),浏览器向服务器发出请求,服务器返回html文件;
2.浏览器开始载入html代码,发现标签内有一个标签引用外部CSS文件;
3.浏览器又发出CSS文件的请求,服务器返回这个CSS文件;
4.浏览器继续载入html中部分的代码,并且CSS文件已经拿到手了;
5.浏览器在代码中发现一个标签引用了一张图片,向服务器发出请求。此时浏览器不会等到图片下载完,而是继续加载后面的代码;
6.服务器返回图片文件,由于图片占用了一定面积,影响了后面段落的排布,因此浏览器需要回过头来重新渲染这部分代码;
7.浏览器发现了一个包含一行Javascript代码的
            

文章作者: 弈心
版权声明: 本博客所有文章除特別声明外,均采用 CC BY 4.0 许可协议。转载请注明来源 弈心 !
评论
  目录