浏览器相关(一)渲染流程与重排/重绘

1,377 阅读5分钟

上图为浏览器的渲染流程

浏览器在接收到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. 如果要频繁访问布局信息,可以缓存起来

zencode.in/18.CSS-anim…

张鑫旭博客:www.zhangxinxu.com/wordpress/2…

www.w3cplus.com/css3/introd…

www.cnblogs.com/yuzhongwusa…