目录
1.浏览器中有哪些主要进程
浏览器进程
主要负责界面显示,用户交互,子进程管理等。浏览器进程内部会启动多个线程处理不同的任务。
网络进程
负责加载网络资源。网络进程会启动多个线程来处理不同的网络任务。
渲染进程
渲染进程启动后,会开启一个渲染主线程,主要负责执行HTML,CSS,JS代码,渲染页面。
默认情况下,浏览器会为每个标签页开启一个新的渲染进程,以保证不同的标签页之间互不影响。
2.事件循环
事件循环又称消息循环,是浏览器渲染主线程的工作方式。
通常,他会开启一个无限循环,每次从消息队列中取出一个任务执行,而其他线程只需要在合适的时候将任务加入到消息队列末尾即可。
过去把消息队列简单的分为微队列和宏队列,这种说法目前已经无法满足复杂的浏览器环境,取而代之的是一种更加灵活多变的处理方式。
根据W3C官方解释,每个任务有不同的类型,同类型的任务必须在同一个队列,不同的任务可以在不同队列。不同的任务队列有不同的优先级,在一次事件循环中有浏览器自行决定任务队列的调度优先级。但浏览器必须有一个微任务队列,且其一定具有最高优先级,必须优先调度执行。
3.异步
JS是一门单线程语言,其运行在浏览器渲染进程的主线程上,一个渲染进程只有一个主线程,而主线程却又承担着渲染页面,执行JS,CSS,HTML等诸多工作。如果使用同步的方式,有可能会造成阻塞,浪费主线程的宝贵时间;还可能导致页面无法及时更新,给用户造成“卡死”现象。
所以采用异步的方式来避免。具体的做法:当某些任务发生时,比如计时器,网络,事件监听等。渲染主线程会将任务交给其他线程取处理,自生立即结束任务的执行,转而执行后续代码。当其他线程完成处理后,会将事先传递的回调函数包装成任务,加到消息队列末尾排队,等待渲染主线程调度执行。
在这种异步模式下,浏览器永不阻塞,从而最限度的保证了单线程的流畅运行。
3.浏览器渲染基本流程
当用户在地址栏输入URL后,浏览器会向服务器发送请求,待网络进程获取到返回的HTML文档后,会生成一个渲染任务,并将其放入消息队列,浏览器主线程则会通过事件循环从消息队列中取出任务,开启渲染流程。
渲染流程主要分为解析HTML,样式计算,布局,分层,绘制,分块,光栅化,画八个步骤,每一个步骤都有严格的输入和输出,上一个步骤的输出会成为下一个步骤的输入。
解析HTML
解析其实就是将收到的字符串形式的HTML文档转化成一个树结构的对象,方便后续步骤的操作,同时给JS开放一些DOM编程接口。
为了提高解析效率,在解析之前,浏览器会开启一个预解析线程,用来下载外部的CSS和JS文件。
在解析过程中,渲染主线程遇到JS执行JS,遇到CSS执行CSS
如果解析到link位置,而外部CSS文件还没下载好,主线程不会等待,而是继续执行后续代码。这是因为CSS的文件下载解析是在预解析线程内进行的,这也是CSS不会阻塞HTML的根本原因。
如果解析到script位置,而外部JS文件还没下载好,主线程会等待JS文件下载完成,并执行完全局代码后,再继续执行后续代码。因为JS的执行可能会修改当前DOM树,这也就是JS会阻塞HTML的根本原因。
第一步完成后会得到一颗DOM树和一颗CSSOM树,浏览器的默认样式,内部样式,外部样式,行内样式均会在CSSOM树中
样式计算
渲染主线程会遍历得到的DOM树,为每一个节点计算出它的最终样式。
在这一步中,许多预设值会变成绝对值,比如red,white会变成对应的RGB值;许多相对单位会变成绝对单位,比如%,rem,vw会变成px。
第二部完成后会得到一颗带有样式的DOM树
布局-layout
主线程会遍历DOM树的每一个节点,计算出它的几何的信息。如:节点的宽高,相对包含块位置
值得注意的是:布局树和DOM总不是一一对应的,比如:
{display:none}的节点,再DOM树中存在,但在布局树中没有几何信息;
伪选择器::after等在布局树中有几何信息,在DOM树种却不存在;
又比如匿名行盒,匿名块盒等
第三步,布局的结果就是布局树
分层
浏览器会采用一套复杂的策略对布局树进行分层。
分层的好处是将来某一层的信息发生了改变,只需要对该层进行后续处理,从而提升效率。
第四步分层的结果就是得到一个个的层信息
绘制
渲染主线程会为每一层单独产生一套绘制指令,用于描述这一层该如何画出来。
第五步绘制的结果就是得到绘制信息
分块
绘制完成后,浏览器会将绘制信息提交给合成线程,后续工作交由合成线程来完成。
合成线程会对每个图层进行分块,划分成更小的块区域。
第六步分块的结果就是得到块信息
光栅化
完成分块后,合成线程会将块信息提交给GPU进程,以极高的速度完成光栅化,并且会优先处理靠近视窗的块。
第七步光栅化的结果就是得到,一块块块的位图
画出屏幕最终成像
合成线程拿到每个层,每个块的位图后,会生成一个个的 [ 指引 (quad) ] 信息。
指引信息会标识出每一个位图应该画到屏幕上哪个位置,以及会考略旋转,缩放等变形。
最后,合成线程会把生成的指引信息提交给GPU进程,GPU进程产生系统调用,将信息提交给GPU硬件,由GPU硬件画出屏幕最终成像。
值得的是:由于变形是发生在合成线程的,与渲染主线程无关,也就不会产生repaint和reflow。这也及时transform效率高的本质原因
reflow
reflow 的本质就是重新计算 layout 树。
当进行了会影响布局树的操作后,需要重新计算布局树,会引发 layout。
为了避免连续的多次操作导致布局树反复计算,浏览器会合并这些操作,当 JS 代码全部完成后再进行统一计算。所以,改动属性造成的 reflow 是异步完成的。
也同样因为如此,当 JS 获取布局属性时,就可能造成无法获取到最新的布局信息。
repaint
repaint 的本质就是重新根据分层信息计算了绘制指令。
当改动了可见样式后,就需要重新计算,会引发 repaint。
由于元素的布局信息也属于可见样式,所以 reflow 一定会引起 repaint。
--------------------------------------------------------------------------------------------------------------------------------
若有错误或描述不当的地方,烦请评论或私信指正,万分感谢 😃