浏览器渲染原理与过程
关键渲染路径
请求资源—解析—构建树—渲染布局—绘制
页面加载过程
分为两部分DOMContentLoaded和load
前者触发时,已加载页面内容,不包括样式表和外部资源
后者,已经加载好
构建DOM树
- 根据html文档构建树,遍历dom节点
- 此过程可能因为css,js加载而阻塞
- display为none和注释、
构建CSSOM树
解析css文件—对应stylesheet对象
- cssom、dom可以同时解析
- css解析和js执行互斥
- webkit做了优化,只有js访问css才阻塞
构建渲染树
遍历DOM树可见节点,找到适配的css应用
渲染树生成后,还是没有办法渲染到屏幕上,渲染到屏幕需要得到各个节点的位置信息,这就需要布局(Layout)的处理了。
渲染树布局
根节点遍历渲染树
renderObject对象,有css样式,根据这些信息在页面中排列
float、fix等布局脱离文档流 = 脱离渲染树
渲染树绘制
遍历渲染树 paint()方法
渲染阻塞问题
js可以修改DOM和CSS,所以遇到script标签,dom树暂停构建,如果是外部的,就先下载在执行
现在有了defer、async两种属性。这两种属性都不会阻塞dom解析,但defer是严格按照引入顺序执行,最后执行domcontentloaded方法;async 谁先加载完就执行。
如果js操作了css,但cssom还没有构建,都停止,先去构建cssom
减少reflow、repaint触发次数
- 用transform做形变和位移可以减少reflow 避免逐个修改节点样式,尽量一次性修改
- 使用DocumentFragment将需要多次修改的DOM元素缓存,最后一次性append到真实DOM中渲染
- 可以将需要多次修改的DOM元素设置display:none,操作完再显示。(因为隐藏元素不在render树内,因此修改隐藏元素不会触发回流重绘)
- 避免多次读取某些属性 通过绝对位移将复杂的节点元素脱离文档流,形成新的Render Layer,降低回流成本
几条关于优化渲染效率的建议
-
合法地去书写HTML和CSS ,且不要忘了文档编码类型。
-
样式文件应当在head标签中,而脚本文件在body结束前,这样可以防止阻塞的方式。 简化并优化CSS选择器,尽量将嵌套层减少到最小。
-
DOM 的多个读操作(或多个写操作),应该放在一起。不要两个读操作之间,加入一个写操作。
-
如果某个样式是通过重排得到的,那么最好缓存结果。避免下一次用到的时候,浏览器又要重排。
-
不要一条条地改变样式,而要通过改变class,或者csstext属性,一次性地改变样式。 尽量用transform来做形变和位移
-尽量使用离线DOM,而不是真实的网页DOM,来改变元素样式。比如,操作Document
Fragment对象,完成后再把这个对象加入DOM。再比如,使用cloneNode()方法,在克隆的节点上进行操作,然后再用克隆的节点替换原始节点。 -
先将元素设为display: none(需要1次重排和重绘),然后对这个节点进行100次操作,最后再恢复显示(需要1次重排和重绘)。这样一来,你就用两次重新渲染,取代了可能高达100次的重新渲染。
-
position属性为absolute或fixed的元素,重排的开销会比较小,因为不用考虑它对其他元素的影响。
-
只在必要的时候,才将元素的display属性为可见,因为不可见的元素不影响重排和重绘。另外,visibility : hidden的元素只对重绘有影响,不影响重排。
-
使用window.requestAnimationFrame()、window.requestIdleCallback()这两个方法调节重新渲染。