浏览器渲染过程及优化
熟练使用Chrome开发工具
浏览器渲染基础
cpu主要负责操作系统和程序 gpu负责显式 数据处理效率更高 gpu.js
- Dom是分层的
firefox的3d插件(已废弃)很好的展现了这个点。
Q:什么元素会分层?
A:根元素,position层,transform、半透明、css滤镜、canvas、video、overflow等 - 对DOM元素节点计算样式结果。(Recalculate Style)
- 为每个节点生成图形位置。(Layout 回流/重排)
- 将每个节点绘制填充到图层位图中。(Paint)
- 把图层作为纹理,上传到GPU
- 把符合的图层生成到页面上。(Composite Layers 合成层)
Q:Composite Layers做了啥?
A:图层的绘制列表,准备好,commit给(合成线程)主线程 合成线程 viewport 划分图块 生成位图的过程 光栅化 Raster 所有图块 合成DarwQuad提交给浏览器渲染进程 viz组件接收到DarwQuad 后,绘制到屏幕上
总结上面过程的主要过程
Layout ==> Paint ==> Composite Layers
渲染引擎结构与工作流程
以HTML、JS、CSS等文件作为输入,以可视化内容作为输出
经过下面步骤:
- Parsing HTML to construct DOM tree
根据html文件生成dom树,框架 - Render Tree Construction
根据css,js文件,在dom树的基础上生成渲染树 - Layout of Render Tree
渲染树布局排版(重排) - Painting Render Tree
排版之后。绘制(重绘) - Display
(Composite Layers 合成层,布局树)后,把符合的图层上传到GPU生成到页面上展示。- 把文档的结构、元素的样式、几何形状和绘制顺序转换在屏幕上的像素。这个过程称为光栅化
- 合成是一种将页面的各个部分分层,分别栅格化,并在一个被称为合成器线程的独立线程中合成页面的技术
- 上传到GPU生成到页面上展示。
浏览器渲染优化
跳过重排重绘 直接触发了GPU执行 这就是硬件加速
Q:什么会触发硬件加速,让GPU加入进来?
A:CSS3D、video、webgl、transform、css滤镜、will-change:transform1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22@keyframes ball-running {
0% {
top:0;
left: 0;
/* transform: translate(0,0); */
}
25% {
top:0;
left: 200px;
/* transform: translate(200px,0); */
}
50% {
top:200px;
left: 200px;
/* transform: translate(200px,200px); */
}
75% {
top:200px;
left: 0;
/* transform: translate(0,200px); */
}
}上面的动画代码会一直触发重排重绘,性能差
下面的动画代码将处理交给GPU放在一个layer层中去处理,跳过重排重绘,性能大大提升1
2
3
4
5
6
7
8
9
10
11
12
13
14@keyframes ball-running {
0% {
transform: translate(0,0);
}
25% {
transform: translate(200px,0);
}
50% {
transform: translate(200px,200px);
}
75% {
transform: translate(0,200px);
}
}Q:什么属性会造成重排重绘,导致性能下降?
A:可以参考这个网站:[csstriggers](https://csstriggers.com/) 盒子模型的盒子变了 读属性时:offset、scroll、client、width会造成重排(下面为解决方案:requestAnimationFrame)
1
2
3
4
5
6// 解决方式: 读写分离(读写读写读写===>读读读写写写)
const width = document.getElementById("xx").width;
requestAnimationFrame(function(){
// 写逻辑
// React Fiber?
})
大量Dom如何优化
以下内容来自京城一灯小程序Day102题
- 缓存Dom对象
操作Dom时,如果有访问Dom的操作。尤其像循环遍历这种时间复杂度较高的操作。
在循环前将父(主)节点先获取到,在循环中就可以直接饮用,不必要循环查询1
2
3
4
5let rootElem = document.querySelector('#app');
let childList = rootElem.child;
for(let i=0;i<childList.length;j++){
//对应操作
} - 文档片段
利用document.createDocumentFragment()
方法创建文档碎片节点,创建的是一个虚拟Dom对象。向这个节点添加Dom节点,修改Dom节点并不会影响到真是的Dom结构。
我们可以利用这一点先将需要修改的Dom一并修改完,保存至文档碎片中,然后用文档碎片一次性的替换真实的Dom节点。与虚拟Dom类似,也达到了不频繁修改Dom而导致的重排和重绘的过程这样只触发一次回流(重排),效率会打打提升。如果需要对元素进行复杂的操作(删减,添加加点),那么我们应当先将元素从页面中移除,然后再对其进行操作。或者将其复制一个(cloneNode()),在内存中进行操作后再替换原来的节点1
2
3
4
5
6let fragment = document.createDocumentFragment();
const operationDomHandler = (fragment)=>{
// 操作
}
operationDomHandler(fragment);
rootElem.replaceChild(fragment, oldDom)1
2
3let cloneNode = old.cloneNode(true);
operationDomHandler(cloneNode);
rootElem.replaceChild(cloneNode, oldDom); - 用
innerHtml
代替高频的appendChild
- 最优的layout方案
批量读,一次性写。
先对一个不在render tree上的节点进行操作,在把这个节点添加回render tree。这样只处罚一次Dom操作。使用requestAnimationFrame()
,把任何导致重绘的操作放入requestAnimationFrame
1
2
3
4
5const width = document.getElementById("xx").width;
requestAnimationFrame(function(){
// 写逻辑
// React Fiber?
}) - 虚拟Dom
js模拟Dom树并对Dom树操作的一种技术。Virtual Dom是一个纯js对象(字符串对象),所以对它操作是高效的。
利用Virtual Dom,将dom抽象为虚拟Dom,在Dom发生改变的时候先将虚拟Dom进行操作,通过Dom diff算法将虚拟Dom和原虚拟Dom的结构对比,最终批量的去修改真是的Dom结构,尽可能的避免了频繁修改Dom而导致的频繁的重排和重绘。
fastdom,处理dom的插件
- 本文标题:浏览器渲染过程及优化
- 本文作者:乔文飞
- 创建时间:2020-07-22 14:32:38
- 本文链接:http://www.feidom.com/2020/07/22/浏览器渲染过程/
- 版权声明:本博客所有文章为作者学习笔记,有转载其他前端大佬的文章。转载时请注明出处。