前言
🙋 知其然,更知其所以然,举一反三,融会贯通
我们从浏览器访问一个网址,然后就能看到对应的网页信息,这个看似简单的操作,实际由浏览器的各个模块协作完成。
浏览器的进程和线程
- 进程是操作系统资源分配的基本单位,进程中包含线程
- 线程是由进程所管理的
为了保证和提升浏览器稳定性和安全性,浏览器采用了多进程和多线程模型。
浏览器的进程:
- 浏览器进程:负责界面的的显示,用户交互,子进程管理,提供存储等
- 渲染进程:浏览器中每个的Tab页卡都有单独的渲染进程,核心用于渲染界面
- 网络进程:主要处理网络资源加载(html,css,js等)
- GPU进程:主要负责3D绘制,提高性能
- 插件进程:负责管理维护chrome浏览器中安装的插件(内置或第三方的)
渲染进程中的线程:
-
GUI渲染线程:负责渲染页面,解析html和css,构建dom树,cssom树,渲染树和绘制页面,重绘和重排也在该线程执行
-
js引擎线程:一个tab页签中只有一个JS引擎线程(单线程),负责解析和执行js,它和GUI渲染线程不能同时执行,只能一个一个来,如果js执行时间过长就在阻塞界面的渲染
-
异步http请求线程:XMLHttpRequest连接后浏览器开的一个线程,比如请求有回调函数,异步线程就会将回调函数加入到事件队列,等待js引擎空闲时执行
-
计时器线程:主要指的是setTimeout和setInterval, 因为js引擎是单线程,如果处于阻塞状态,那么计时器就会不准了,所以需要单独的线程来处理计时工作。
-
事件触发线程:主要用来控制事件循环,比如js执行定时器,或异步请求时,会将回调函数添加到事件队列中,等待js引擎空闲时,事件触发线程就会将队列中的任务拿到主线程(js引擎线程)去执行
网页加载过程
以第一次加载网页为例(没有缓存),主要的步骤如下:
1. DNS解析
浏览器会通过DNS服务器解析域名得到对应的IP地址
2. Tcp连接
通过Ip和端口与服务器之间建立Tcp连接,这个过程通常称为三次握手
第一次握手
开始建立连接:客户端发送连接请求报文段,设置SYN=1, Seq=x(x为一个随机数)。客户端端进入SYN_SEND状态,等待服务器的确认
第二次握手
服务器收到SYN报文段:服务器收到客户端的SYN报文段后,会进行回执,此时服务器会给客户端发送SYN=1,Seq=y,Ack = x + 1的报文段,已确认建立的是同一个连接
第三次握手
客户端收到SYN+ACK报文段:客户端收到服务器的SYN+ACK报文段后,会向服务器再次发送ACK报文段,设置ACK=y + 1, 服务器收到后确认完毕,客户端和服务器端都进入ESTABLISHED状态,完成Tcp三次握手
3. Http请求
创建Tcp连接后,浏览器就可以向服务器发送Http请求,服务器接收到请求之后根据url的路径进行处理,然后把Html资源文件返回给浏览器,这时浏览器就可以开始解析Html文档了。
4. 断开Tcp连接
为了避免服务器与客户端双方的资源占用和损耗,当双方没有请求和响应时,任意一方都可以主动的发送关闭请求,这个过程通常称为四次挥手
第一次挥手
浏览器客户端主动向服务器发送报文段FIN, seq = x+2, ACK= y +1, 告诉服务器我已经没有数据要发送了,要断开连接,此时不能确定服务器是否还会给客户端端发送消息
第二次挥手
服务器收到客户端的报文段后,会给客户端一个回执消息,设置ACK = x + 3, 告诉客户端我已经收到你的断开消息
第三次挥手
在第二次挥手的基础上,服务器同时也会发送一个报文段为FIN seq = y + 1的消息,告诉客户端我也没有消息发送了,要断开连接
第四次挥手
客户端收到FIN的报文段后,会再次给服务器发送一个ACK= y + 2的报文段,告诉服务器我也收到你断开的消息,那我们就断开吧
5.浏览器解析渲染
先看一张图了解一下渲染的过程
主要流程分为如下几个步骤:
- 解析HTML,构建DOM树(文档对象模型树)
- 解析CSS, 构建CSSOM树(css规则树)
- 合并DOM树和CSS树,生成render树(渲染树或者叫布局树)
- 根据render树进行布局(layout/reflow), 计算每个元素的位置以及尺寸
- 通过布局树,进行分层(根据定位属性,透明属性,transform属性,clip属性等)生成图层树
- 将不同的图层进行绘制,然后浏览器将不同的图层信息发送给GPU,GPU将各图层合成, 最终显示在网页上
解析过程中细节问题:
- 服务器端返回的类型是text/html时,浏览器会将收到的数据通过HTMLParser进行解析(边下载边解析)
- 在解析前会执行预解析操作,会预先加载js,css文件
- 然后依次是字节流-->分词器-->Tokens-->根据token生成节点-->插入到DOM树中
- 遇到js:如果在解析过程中遇到script标签,HTMLParse则会停止解析,浏览器会去下载并执行对应的js脚本
- 在js执行前,需要等待当前脚本之上的所有css加载解析完毕(js的执行依赖css的加载)
css样式文件尽量放在页面头部,css加载不会阻塞DOM Tree解析,浏览器会用解析的DOM Tree和CSSOM进行渲染,这样不会出现页面闪烁问题,如果css放在页面底部,浏览器是边解析边渲染的,渲染出的结果不包含样式,后续会发生重绘操作。
js文件放在页面底部,防止js的加载,解析,执行阻塞页面后续的正常渲染