渲染引擎/浏览器内核
渲染进程的下属线程结构
- GUI渲染线程:负责渲染浏览器界面,解析HTML/CSS构建dom树和renderObject树,布局绘制,重绘回流任务。
- JS引擎线程:解析js脚本,单Tab页的js线程为单线程,与GUI线程互斥并且GUI会等待js线程执行。
- 定时触发器堆线程:setInterval 和 setTimeout等,保证不会因js线程阻塞影响时钟准确性。
- 事件触发线程:eventloop消息队列线程。
- http请求异步线程:进行XMLHttpRequest新建线程,结束后关闭线程并放入消息队列。
- 用户定义的webworker线程(子线程完全受主线程控制不可操作dom不影响js单线程特性)。
渲染机制
- 主渲染线程处理html构建dom树
- stylesheets的格式化/标准化/计算节点样式(继承+层叠)(根继承userAgent)
- 整合两个树构建渲染树
- 根据渲染树布局,计算节点位置(layout布局树)
- 生成图层树(layer tree)
- 对每个图层,根据渲染树生成很多绘制指令,渲染进程把这些指令commit给
合成线程
绘制。 - 合成线程将图层分为图块,再根据图块位置优先级将其栅格化为位图(一般会有一个栅格化线程池不断把图块栅格化)(一般会调用GPU加速快速栅格化并将位图保存在显存中)。
- 栅格化完成,合成线程生成绘制命令
DrawQuad
给浏览器进程合成页面,浏览器根据此命令绘制页面进入内存后发给显卡,显卡渲染到显示器上。 - 显卡拿到页面后合成显示器图像,按刷新率将缓冲区的图像显示在显示器上。(掉帧因为刷新之前新的页面没有绘制成功)
cssom
CSSOM树构建时会阻塞渲染,且这是十分消耗性能的过程,所以在代码层面应当尽量使层级扁平化,越是具体的css选择器执行速度也越慢。
js解析
script标签与js的执行会阻塞dom构建。
defer异步加载,dom构建完毕后执行(按加载顺序)。
async异步加载,加载完阻塞dom执行(顺序谁快谁加载)。
- 因为js可以操作dom,会影响GUI线程的dom渲染,所以是互斥的。
图层树的两种
显示图层 / 创建新BFC的方法
- float的值不是none。
- position的值不是static或者relative。
- display的值是inline-block、table-cell、flex、table-caption或者inline-flex
- overflow的值不是visible(需要裁剪内容)
隐式图层
Z-index为底部图层,大于他的为新图层。(可能造成图层爆炸)
特别内容
- Load事件表示所有dom/css/js/img都加载完毕。
- DOMContentLoaded表示HTML解析结束。
- 频繁渲染的元素生成新的BFC来避免引发过多回流,但是也不能太多
- 元素几何属性该表引起回流必定引发重绘
回流重绘
- 回流从三个树的改变开始所有环境重新进行。
- 重绘在重新计算stylesheets后直接去分块生成位图。
event loop 与回流重绘
- event loop执行完微任务会判断document是否需要更新,浏览器为60hz每16ms判断更新一次。
- 判断是否有resize或scroll,若有则触发。
- 判断是否触发了@media
- 更新动画发送事件
- 判断是否有全屏操作事件
- 执行
requestAnimationFrame
回调 - 执行
IntersectionObserver
回调,用于判断元素是否可见,可以用于懒加载 - 更新界面
- 空余事件执行
requestIdleCallback
减少回流与重绘
- 合并样式修改
- 击中修改时使其进入新的BFC更改结束后再回到文档流或者隐藏再出现
- 避免重绘操作中获取重绘影响值(每次获取重绘影响值都会刷新重绘任务队列而不是等待下一帧)
- 触发CSS3硬件加速不会引起回流重绘(transform/opacity/filters/Will-change)
合成/GPU加速
CSS3的硬件加速属性实现合成的效果/实现GPU加速的效果。
跳过布局和绘制流程直接给合成线程处理进行GPU渲染。
不会影响主要渲染线程的运行。
性能优化
- 批量的DOM操作减少重绘回流频率/防抖节流处理。
- 动画使用GPU加速。
- 频繁操作的dom临时脱离文档流或者隐藏。
- 对频繁操作的元素生成单独的图层。(will-change或不支持时使用3D transform强制提升图层)