上图为浏览器的渲染流程
浏览器在接收到HTTP的响应后就开始渲染了,流程如下
一. 渲染主线程的工作:
1. 当接收到响应体的数据后,浏览器就开始解析HTML文本
2. 逐行解析HTML文本,如果遇到,
3. 解析完HTML文本得到DOM树,
3.1 触发readystatechange事件,状态为interactive
3.2 触发DomCompletedLoad事件,标志HTML文本解析完成,但是仍有子资源在加载中
4. 生成完整的CSSOM(CSS可以内联,也可以外部引入),
4.1 生成styleSheet(可以通过document.styleSheet查看)
4.2 属性标准化转换
4.3 计算DOM树每个节点的样式(CSS继承,权重)
5. 所有文档/子资源加载完毕
5.1 触发readystatechange事件:Complete
5.2 触发load事件
5.4 触发pageShow事件
6. 生成布局树(只包含可见元素):布局树需要DOM解析完成及CSSOM生成后才可以生成。
7. 生成图层树(CSS的transform,opacity,scrollbar, 溢出的子元素, z-index等会有单独的图层),其中图层树也是有继承关系的
8. 生成绘制指令
9. commit给合成线程
二. 合成线程的工作:
10. 讲图片分块(512512, 256256)
11. 光栅化处理:为每一个图片分配资源,并交给光栅化线程(光栅化线程是由渲染主线程处理的)处理
13. 光栅化处理完毕后发送DrawQuard指令给浏览器主进程。
三. 光栅化线程的工作:
12. 与GPU进程进行通讯,使用GPU加速生成图片
四. 浏览器主进程
14. 接受到DrawQuard消息后会将图片显示在屏幕上
页面阻塞
由上述的渲染流程可以看出DOM树和CSS样式的改变会影响后续的渲染执行。
1. JS阻塞页面的情况
在浏览器中,JS的解析执行也是在渲染主线程上执行的,因此当在解析DOM的过程中遇到了JS代码时候,由于浏览器无法预知脚本的内容,因此就会停止DOM的解析,转而先执行JS脚本,因此会导致JS阻塞页面的情况。
2. CSS加载阻塞页面的情况
对于外部引入的CSS文件,会调用网络进程异步下载文件,因此CSS文件的加载是不会影响DOM树的解析。但是渲染树的生成需要CSSOM解析完成后才能进行,如果CSS加载出现问题就会导致页面白屏。
但是如果CSS后面紧跟着JS脚本,由于JS可以修改DOM与CSS样式,因此在不清楚JS内容的情况下,浏览器会暂停DOM解析,并解析完CSSOM,然后执行JS脚本,因此CSS加载会阻塞DOM树的渲染,会阻塞JS脚本的执行。
构建CSSOM <-> 执行JS <-> 继续渲染DOM 三者事件是由依赖关系的。
页面加载优化
1. 利用
defer属性:在HTML完成解析之后,DomContentLoad事件触发之前,因此不会阻塞页面解析;
async属性:在脚本下载过程中不会阻塞页面解析,但是在下载之后会立即执行,阻塞页面(但是比不加任何属性要好)
2. 通过内联CSS/JS
3. 尽量减少文件大小,比如压缩文件,去除注释等
4. 利用媒体查询
5. CDN加速,优化下载时间
相关事件
上述流程中会触发浏览器的一系列事件:
load事件:在点击浏览器前进,后退按钮的时候,浏览器其实是从往返缓存中读取历史页面,然后渲染出来,在这种情况下点击前进/后退按钮加载页面是不会触发load事件的
pageShow事件:pageShow事件不会受到缓存的影响,可以监听此事件来执行JS脚本,另外,有event.persisted只读属性,其返回值为布尔值,返回ture表明此页面是从缓存中读取出来的
重排/回流与重绘
简单来说:重排就是DOM元素重新排布,比如元素增删;重绘是指元素颜色等不会影响布局的属性发生变化,从而重新绘制图片。
参考链接:www.cnblogs.com/cencenyue/p…
页面布局和属性发生改变时就要回流/获取DOM尺寸等信息的时候也会引起重排,因为浏览器要通过重新渲染来获取相关信息
参考链接:segmentfault.com/a/119000001…
优化措施:
1. 使用cssText或者类名修改样式(对于老的浏览器)
2. 批量修改DOM:浏览器本身就有队列来存储多次修改事件,因此优化不明显
1)利用display:none/block来 隐藏 -> 修改 -> 重新显示
2)使用createDocumentFragment
3) 克隆节点,再替换原始元素
3. 避免触发同步布局事件:
正常情况下的布局操作:通过 DOM 接口执行添加元素或者删除元素等操作后,是需要重新计算样式和布局的,不过正常情况下这些操作都是在另外的任务中异步完成的,这样做是为了避免当前的任务占用太长的主线程时间
同步布局,是指 JavaScript 强制将计算样式和布局操作提前到当前的任务中。
4. 对于复杂的动画效果,使用绝对定位让其脱离文档流,从而致使重绘而不是一个完整的回流
5. 指定z-index等使用,认为指定分层
6. 避免使用table布局,因为一个td更改会导致整个table重新渲染
7. 使用will-change,预先分配资源处理这类事件
8. 使用transform来控制动画:3D transform会启用GPU加速,并且浏览器做的只是图层的重新组合而已,**不会触发重排/回流与重绘 **交给GPU处理
9. opacity **不会触发重排/回流与重绘 ****交给GPU处理 **
10. filter属性 触发GPU渲染 交给GPU处理
10. 如果要频繁访问布局信息,可以缓存起来