- Published on
浏览器基础知识
- Authors
- Name
- Yanbin
- @ybtaimu
Preconditions:Chrome Version >= 72
多进程架构
1.Browser Process(浏览器进程):负责浏览器的TAB的前进、后退、地址栏、书签栏的工作和处理浏览器的一些不可见的底层操作
2.Renderer Process(渲染进程):一个Tab内的显示相关的工作,也称渲染引擎。
3.Network service process(网络进程):处理网络请求,从网上获取数据
4.Plugin Process(插件进程):网页使用到的插件
5.GPU Process(插件进程):整个应用程序的GPU任务
网页渲染原理
构建DOM
- 主线程会解析数据转化为DOM(Document Object Model)对象。
资源子加载
- 构建DOM的过程中,会解析到图片、CSS、JavaScript脚本等资源请求,逐一发起请求去获取
- 为了效率,浏览器也会运行预加载扫描(preload scanner)程序,若HTML中存在img、link等标签,预加载扫描程序会把这些请求资源下载
JavaScript的下载与执行(重点 js引擎线程与GUI 渲染主线程互斥)
- 遇到<script>标签,渲染引擎会停止对HTML的解析,而去加载执行JS代码,
原因在于JS代码可能会改变DOM的结构
(比如执行document.write()等API) - 如果在<script> 标签上添加了
async
或defer
等属性,浏览器会异步的加载和执行
JS代码,而不会阻塞渲染
样式计算 - Style calculation
- 计算样式是主线程根据CSS样式选择器(CSS selectors)计算出的每个DOM元素应该具备的具体样式
- 主线程在解析页面时,遇到<style>标签或者<link>标签的CSS资源,会加载CSS代码,根据CSS代码确定每个DOM节点的计算样式(computed style)
布局 - Layout
- DOM树和计算样式完成后,需要知道节点在页面上的
位置
,布局(Layout)其实就是找到所有元素的几何关系
的过程 - 主线程会遍历DOM 及相关元素的计算样式,构建出包含每个元素的页面坐标信息及盒子模型大小的布局树
(Render Tree)
,遍历过程中,会跳过隐藏的元素(display: none),另外,伪元素虽然在DOM上不可见,但是在布局树上是可见的
绘制 - Paint
- 每个元素的绘制先后顺序
- 在绘制阶段,主线程会遍历布局树(layout tree),生成一系列的绘画记录
(paint records)
。绘画记录
可以看做是记录各元素绘制先后顺序的笔记
合成 - Compositing
- 文档结构、元素的样式、元素的几何关系、绘画顺序,这些信息我们都有了,这个时候如果要绘制一个页面,我们需要做的是把这些
信息转化为显示器中的像素
,这个转化的过程,叫做光栅化(rasterizing)
。 - Chrome采取一种更加复杂的叫做
合成(compositing)
的做法。合成是一种将页面分成若干层
,然后分别对它们进行光栅化,最后在一个单独的线程 -合成线程(compositor thread)里面合并成一个页面的技术
。 - 对元素进行分层,确定哪些元素需要放置在哪一层,主线程需要遍历渲染树来创建一棵
层次树(Layer Tree)
。 - 合成线程会收集图块上面叫做绘画四边形(draw quads)的信息来构建一个
合成帧(compositor frame)
。完成后合成线程就会通过IPC向浏览器进程(browser process)提交(commit)一个渲染帧。这个时候可能有另外一个合成帧被浏览器进程的UI线程(UI thread)提交以改变浏览器的UI。这些合成帧都会被发送给GPU从而展示在屏幕上。如果合成线程收到页面滚动的事件,合成线程会构建另外一个合成帧发送给GPU来更新页面。 - 合成器线程可以独立于主线程之外通过已光栅化的层创建组合帧,例如页面滚动,如果没有对页面滚动绑定相关的事件,组合器线程可以独立于主线程创建组合帧,如果页面绑定了页面滚动事件,合成器线程会等待主线程进行事件处理后才会创建组合帧
浏览器对事件的处理
点击事件(click event)为例. 当鼠标点击页面时,首先接受到事件信息
的是Browser Process
,但是Browser Process只知道事件发生的类型和发生的位置,具体怎么对这个点击事件进行处理,还是由Tab内的Renderer Process
进行。
渲染进程中合成器线程接收事件
- 合成器线程可以独立于主线程之外通过已光栅化的层创建组合帧,例如页面滚动,如果
没有对页面
滚动绑定相关的事件
,组合器线程可以独立于主线程创建组合帧
,如果页面绑定
了页面滚动事件
,合成器线程会等待主线程进行事件处理后才会创建组合帧
。 - 由于执行 JS 是主线程的工作,当页面合成时,合成器线程会标记页面中
绑定有事件处理器的区域
为非快速滚动区域(non-fast scrollable region)
,如果事件发生在这些存在标注的区域
,合成器线程会把事件信息发送给主线程,等待主线程进行事件处理,如果事件不是发生在这些区域,合成器线程则会直接合成新的帧而不用等到主线程的响应
查找事件的目标对象(event target)
- 当合成器线程接收到事件信息,判定到事件
发生不在非快速滚动区域
后,合成器线程会向主线程发送这个事件信息,主线程获取到事件信息的第一件事就是通过命中测试(hit test)去找到事件的目标对象
。具体的命中测试流程是遍历
在绘制阶段生成的绘画记录(paint records)
来找到包含了事件发生坐标上的元素对象
。
浏览器对事件的优化
- 一般我们屏幕的帧率是每秒60帧,也就是
60fps
,但是某些事件触发的频率超过了这个数值,比如wheel,mousewheel,mousemove,pointermove,touchmove,这些连续性的事件
一般每秒会触发60~120次,假如每一次触发事件都将事件发送到主线程处理,由于屏幕的刷新速率相对来说较低,这样使得主线程会触发过量的命中测试以及JS代码,使得性能有了没必要是损耗。 - 出于优化的目的,浏览器会
合并这些连续的事件
,延迟到下一帧渲染时执行,也就是requestAnimationFrame之前
。 非连续性的事件
,如keydown,keyup,mousedown,mouseup,touchstart,touchend等,会直接派发给主线程去执行
Quoter:
下面几个链接的内容差不多