chrome浏览器使用的是多进程多线程模式,因为现在的网页复杂性非常高。如果整个浏览器是单进程的,有可能某个page界面的抛错就会导致整个浏览器的crash。同时多个界面互相可以访问相同的内存和相同的执行环境,安全性也是一个大的问题,所以浏览器需要采用多进程模式。
- 1,浏览器主进程(Browser进程):控制chrome的地址栏,书签栏,返回和前进按钮,同时提供存储功能
- 2,第三方插件进程:每种插件一个进程,插件运行时才会创建
- 3,浏览器渲染进程(浏览器内核,内部是多线程的):负责界面渲染,脚本执行,事件处理等
- 4,GPU进程:用于3D绘制
- 5,网络进程:主要负责页面的网络资源加载,之前是作为一个模块运行在浏览器进程里边的,最近独立为了一个进程
浏览器主进程
网页之外的内容都由浏览器主进程控制。有
- 绘制浏览器的按钮和输入框的UI线程
- 处理网络堆栈从网络中获取数据的网络线程
- 控制文件的权限的存储线程
1. 处理地址栏输入
用户在地址栏输入之后,浏览器进程中的UI线程回去判断用户的输入是url还是一个搜索项,因为chrome的地址栏也可以作为一个搜索输入框来使用。所以UI线程需要判断用户的的输入然后决定把用户的输入发送给搜索引擎还是发起网络请求。
还有其他类型比如ftp://是打开本机文件,chrome://可以查看浏览器版本等浏览器设置,dingtalk://是在网页唤起本机钉钉
2. 处理url请求
浏览器进程会通过IPC把URL请求发送至网络进程,网络进程接收到URL请求后,会发起真正的URL请求。
首先网络进程会查找本地缓存是否有该资源,有就直接返回,没有就进入请求流程。会先经过DNS解析-》建立TCP链接-》接收返回信息-》解析响应头
3,响应数据处理
在网络请求完成之后,服务器返回数据给浏览器,但是服务器返回的类型有很多种,浏览器会根据响应头的Content-Type来判断返回值的类型,如果服务端返回的是数据是HTML,网络线程会把数据直接发送给渲染进程(浏览器内核),但是如果是zip文件或者是其他类型的文件,这代表着这是一次下载请求,网络线程会把数据发送给下载管理器。 同时这个过程也会发生浏览器的安全检查,如果响应域和返回数据与已知的恶意站点所匹配,网络线程会告诉渲染进程展示一个警告界面,同时为了确保不会把危险的跨域数据发送给渲染进程也会进行CORB检测。
4,准备渲染进程
当所有的检查都完成后,网络进程确定浏览器应该导航到对应的请求站点,网络进程告诉浏览器进程数据已经准备完毕,然后浏览器进程便开始去准备渲染进程。
不同源的界面,会有一个单独的渲染进程,如果是从同源的A界面打开B界面,则B界面的渲染进程会复用A界面的渲染进程。
由于网络请求需要几百毫秒才能得到响应返回,浏览器应用了一种用来加速这个过程的优化策略,当浏览器进程发送一个网络请求给网络进程的同时, 浏览器进程会主动尝试去查找或启动一个渲染进程,这个过程是并行执行的。使用这种方法,如果一切进展顺利的话,当网络进程接收到数据时,渲染进程已经处于待机状态了,当然如果请求被重定向的话则不会用到这个准备好的渲染进程。
渲染进程准备好之后,还不能立即进入文档解析状态,因为此时的文档数据还在网络进程中,并没有提交给渲染进程,所以下一步就进入了提交文档阶段。
5,提交文档
这里的“文档”是指URL请求的响应题数据
- "提交文档"的消息是由浏览器进程发出的,渲染进程接收到"提交文档"的消息后,会和网络进程建立传输数据的"管道".
- 文档数据传输完成之后,渲染进程会返回“确认提交”的消息给浏览器进程。
- 浏览器进程在接收到“确认提交”的消息后,会更新浏览器界面状态,包括安全状态,地址栏的URL,前进后退的历史状态,并更新Web界面。
渲染进程
渲染器进程的主要工作是将HTML,CSS和JavaScript转换为用户可以与之交互的网页。
在渲染进程中,主线程处理大部分的代码,同时如果使用了web worker或者service worker,则工作线程也会处理一部分代码。合成器和光栅线程也运行在渲染进程中来高效,流畅的渲染界面。
6.构造DOM
当渲染进程接收到导航过程中浏览器进程的提交文档信息并开始接收HTML数据时,主线程开始解析HTML并将其转换为DOM。 DOM是能够被浏览器理解的结构,并且通过JavaScript可以进行修改。
当HTML解析器遇到了script标签,它会暂停HTML文档的解析,并且去加载-》解析-》执行js代码,因为js中可能存在类似于document.write这样的改变DOM结构的语句。
7.样式计算
样式计算的目的是为了计算出DOM节点中每个元素的具体样式,这个阶段大体分为三步来完成。
- 把CSS转换为浏览器能够理解的结构,和HTML文件一样,浏览器也是无法直接理解这些纯文本的CSS样式,所以当渲染引擎接收到CSS文本,会将CSS文本转换为浏览器可以理解的结构---styleSheets。并且该结构同时具备了查询和修改功能,也就是后续的样式操作。
- 转换样式表中的属性值,使其标准化,也就是对styleSheets的属性值进行标准化操作。
- 计算出DOM树中每个节点的具体样式,通过CSS的继承规则和层叠规则,最终会输入每个DOM节点的样式,并被保存在ComputedStyle的结构内。
8.布局阶段
有了DOM树和DOM树上的元素的样式后,我们还不知道DOM树上的元素在屏幕上的具体位置,接下来就是要计算DOM树中可见元素的几何位置,这个过程就是布局。
8.1 创建布局树
DOM树中display:none的元素是不可见的,所以需要根据DOM树和样式计算创建一个可见元素布局树。
8.2 分层
在得到了DOM树, DOM树上元素的样式,每个元素在页面上的几何位置之后,还不能直接渲染出我们想要的界面,我们还需要知道渲染界面时元素的渲染位置。 例如当某个元素上有 z-index属性时,就不能按照元素原有的顺序进行绘制,否则就会出错
8.3 合成
现在渲染进程知道了DOM结构,每个元素的样式,元素在页面上的几何位置,元素的渲染先后。渲染进程将这些信息转换为屏幕上的像素的过程被叫做栅格化。
一种最简单原始的做法就是 只对用户浏览器当前可视化的部分做栅格化,当用户滚动页面时,再通过栅格化去填补缺失的部分。但是目前现代浏览器运行着一个更复杂的过程,就是合成。
合成是一种将页面的各个部分进行分层的技术,分别对不同的图层进行栅格化,并在合成线程中作为页面进行合成。因为图层都已经被栅格化,在发生滚动的时候,只需要进行图层的移动来合成一个新的帧就行了。
从上图可知,当图层的绘制列表准备好之后,主线程会把该绘制列表提交给合成线程,因为有些图层可能会很大,比如有些界面需要滚动好久才能滚动到底部,这时候,合成线程就会将图层划分为图块来优先满足 浏览器可视区域的渲染,这些图块的大小通常是256X256或者 512X512, 然后合成线程会按照 可视区域附近的图块来优先生成位图,这里生成位图的操作就是栅格化。
同时渲染进程维护了一个栅格化的线程池,所有图块栅格化都是在线程池中执行的:
栅格化的过程会使用GPU来加速生成,使用GPU生成位图的过程叫做快速栅格化,或者GPU栅格化,生成的位图都被保存在GPU内存中。
一旦所有的图块都被栅格化,合成线程就会生成一个绘制图块的命令--“Draw Quad”, 然后将该命令提交给浏览器进程。
浏览器进程接收到命令后,会将页面内容绘制到内存中,最后再将内存显示在屏幕上。
总结
到这里,浏览器的整体进程交互和渲染过程就结束了。总结下来就是:
- 渲染进程将HTML内容转换为DOM树
- 渲染引擎将CSS样式转换为styleSheets,计算出DOM节点的样式;
- 创建布局树,并计算出布局树的布局信息(几何信息);
- 对布局树进行分层,生成分层树;
- 每个图层生成绘制列表,提交至合成线程;
- 合成线程将图层分为图块,并在光栅化线程池中将图块转换为位图, 中间可能伴随有GPU加速;
- 合成线程发送 DrawQuad给浏览器进程。
- 浏览器进程根据命令消息生成页面,显示到显示器上。
重排:
当使用js 改变某个元素的大小,宽高,会引起重新计算布局,分层,绘制,合成等步骤。\
重绘:
当使用js 改变某个元素的颜色,会引起 重新绘制,光栅化,合成等步骤,节省了布局和分层阶段,相较于重排执行效率会高一些。
userAgent
用户代理
在控制台输入navigator.userAgent
PC端: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/104.0.0.0 Safari/537.36
手机端:Mozilla/5.0 (iPhone; CPU iPhone OS 13_2_3 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/13.0.3 Mobile/15E148 Safari/604.1
作用
- 判断浏览器类型
- 判断是否移动端
- 注意userAgent伪装成本很低,不要过于依赖