前言:渲染树
- 浏览器使用流式布局模型,浏览器将
HTML文件转换成DOM树,将CSS文件转换成CSSOM树,DOM和CSSOM这两棵树组合为渲染树,即Render Tree。看如下图所示:
-
渲染树只会包括需要显示的节点和这些节点的样式信息,计算他们在页面上的位置和大小,如果某个节点是 display: none的,那么就不会在渲染树中显示。
-
当浏览器生成渲染树以后,就会根据渲染树来进行布局(也可以叫做回流),然后调用 GPU 绘制,合成图层,显示在屏幕上。
什么情况阻塞渲染
-
首先渲染的前提是生成渲染树,所以
HTML和CSS肯定会阻塞渲染。如果你想渲染的越快,你越应该降低一开始需要渲染的文件大小,并且扁平层级,优化选择器。 -
然后当浏览器在解析到
script标签时,会暂停构建DOM,完成后才会从暂停的地方重新开始。也就是说,如果你想首屏渲染的越快,就越不应该在首屏就加载 JS 文件,这也是都建议将script标签放在body标签底部的原因。当然,并不是说
script标签必须放在底部,因为你可以给script标签添加defer或者async属性。当
script标签加上defer属性以后,表示该 JS 文件会并行下载,但是会放到HTML解析完成后顺序执行,不会影响到DOM结构。所以对于这种情况你可以把script标签放在任意位置。defer会在DomContentLoaded和Load事件之前执行。当
script标签加上async属性以后,表示该 JS 文件的下载会与DOM解析并行,但是async下载完之后会立即执行。所以会影响到DOM结构。如果项目中的脚本建存在依赖关系,不推荐使用async。async会在Load事件之前执行,但不能确保与DomContentLoaded的执行先后顺序。
回流 refolw
当Render Tree中部分或全部元素的尺寸、结构、或某些属性发生改变时,浏览器重新渲染部分或全部文档的过程称为回流。
导致回流的操作:
- 页面首次渲染
- 浏览器窗口大小发生改变
- 元素尺寸或位置发生改变
- 元素内容变化(文字数量或图片大小改变而引起的计算值宽度和高度改变)
- 元素字体大小变化
- 添加或者删除可见的
DOM元素 - 激活
CSS伪类(例如::hover) - 查询某些属性或调用某些方法
offsetWidth,width,clientWidth,scrollTop/scrollHeight的计算, 会使浏览器将渐进回流队列Flush,立即执行回流。
重绘repaint
当页面中元素样式的改变并不影响它在文档流中的位置时(例如:color、background-color、visibility等),浏览器会将新样式赋予给元素并重新绘制它,这个过程称为重绘。
总结
回流必定会发生重绘,重绘不一定会引发回流。
回流所需的成本比重绘高的多,改变父节点里的子节点很可能会导致父节点的一系列回流。
如何避免
CSS
- 避免使用
table布局,可能很小的一个小改动会造成整个table的重新布局 - 尽可能在
DOM树的最末端改变class。 - 避免设置多层内联样式。
- 将动画效果应用到
position属性为absolute或fixed的元素上。 - 动画实现的速度的选择,动画速度越快,回流次数越多,也可以选择使用
requestAnimationFrame - 避免使用
CSS表达式(例如:calc()) - 使用
transform替代top - 使用
visibility替换display: none,因为前者只会引起重绘,后者会引发回流(改变了布局) - 将频繁重绘或者回流的节点设置为图层,图层能够阻止该节点的渲染行为影响别的节点。
JavaScript
- 避免频繁操作样式,最好一次性重写
style属性,或者将样式列表定义为class并一次性更改class属性。 - 避免频繁操作DOM,创建一个
documentFragment,在它上面应用所有DOM操作,最后再把它添加到文档中。 - 也可以先为元素设置
display: none,操作结束后再把它显示出来。因为在display属性为none的元素上进行的DOM操作不会引发回流和重绘。 - 避免频繁读取会引发回流/重绘的属性,如果确实需要多次使用,就用一个变量缓存起来。
- 对具有复杂动画的元素使用绝对定位,使它脱离文档流,否则会引起父元素及后续元素频繁回流。
提示如何加速
- 从文件大小考虑
- 从
script标签使用上来考虑 - 从
CSS、HTML的代码书写上来考虑 - 从需要下载的内容是否需要在首屏使用上来考虑
如果这篇文章对您有帮助,记得点一下赞哦!
掘金小册