推荐了解的知识:基本的html,基本的javascript,以及一些css工作原理方面的知识
浏览器的渲染原理
- css的加载和解析不会阻塞html文档的解析
- css的解析会阻塞js的执行,必须等到CSSOM生成后才能执行js
- js的执行会阻塞html文档的解析
- html一边解析一边显示
- css必须完全解析完毕才能进入生成渲染树环节
???????浏览器向服务器请求到了HTML文档后便开始解析,产物是DOM Tree(文档对象模型),如果有css,会根据css生成CSSOM(CSS对象模型),然后再由DOM和CSSOM合并产生Render Tree渲染树,有了渲染树,知道了所有节点的样式,便根据这些节点以及样式计算它们在浏览器中确切的大小和位置,这就是布局。最后把节点绘制到浏览器上。
创建DOM树—创建CSSOM树—执行脚本—生成渲染树—生成布局—绘制
回流
???????回流(reflow)当render tree中的一部分(或全部)因为元素的规模尺寸,布局,隐藏等改变时,网络浏览器重新渲染部分或全部文档的过程。
重绘
???????当页面元素样式改变不影响元素在文档流中的位置时(如background-color,border-color,visibility),浏览器只会将新样式赋予元素并进行重新绘制操作。
???????因为回流(reflow)在浏览器中属于一种用户主导的模块化操作,所以知道如何去改进回流(reflow)时间以及知道各种文档属性(DOM节点深度,css的渲染效率,各种各样的样式改变)对回流(reflow)时间的影响对于开发人员讲是很有帮助的。有时候,即使仅仅回流一个单一的元素,也可能要求它的父元素以及任何跟随它的元素也产生回流。
何时发生
???????有大量的用户行为以及潜在的DHTML改变会触发回流(reflow)。例如,改变浏览器窗口的大小,使用一些JavaScript方法,包括计算样式,对DOM进行元素的添加或删除,或是改变元素的class等。
- 添加或者删除可见的DOM元素;
- 元素位置改变;
- 元素尺寸改变——边距、填充、边框、宽度和高度
- 内容变化,比如用户在input框中输入文字,文本或者图片大小改变而引起的计算值宽度和高度改变
- 页面渲染初始化
- 浏览器窗口尺寸改变——resize事件发生时
- 计算 offsetWidth 和 offsetHeight 属性
- 设置 style 属性的值
???????回流必将引起重绘,而重绘不一定会引起回流。
性能优化
???????回流比重绘的代价要更高,回流的花销跟render tree有多少节点需要重新构建有关系;浏览器本身能够进行优化,尽可能减少重绘和回流。
???????如果每行JS代码操作DOM都需要回流重绘的话,浏览器可能就会受不了。所以很多浏览器都会优化这些操作,浏览器会维护1个队列,把所有会引起回流、重绘的操作放入这个队列,等队列中的操作到了一定的数量或者到了一定的时间间隔,浏览器就会flush队列,进行一个批处理。这样就会让多次的回流、重绘变成一次回流重绘。
???????当你请求向浏览器请求一些 style信息的时候,就会让浏览器flush队列,比如:
- offsetTop, offsetLeft, offsetWidth, offsetHeight
- scrollTop/Left/Width/Height
- clientTop/Left/Width/Height
- width,height
- 请求了getComputedStyle(), 或者 IE的 currentStyle
???????当你请求上面的一些属性的时候,浏览器为了给你最精确的值,需要flush队列,因为队列中可能会有影响到这些值的操作。即使你获取元素的布局和样式信息跟最近发生或改变的布局信息无关,浏览器都会强行刷新渲染队列。
如何减少回流、重绘
var s = document.body.style;
s.padding = "2px"; // 回流+重绘
s.border = "1px solid red"; // 再一次 回流+重绘
s.color = "blue"; // 再一次重绘
s.backgroundColor = "#ccc"; // 再一次 重绘
s.fontSize = "14px"; // 再一次 回流+重绘
// 添加node,再一次 回流+重绘
document.body.appendChild(document.createTextNode(‘abc!‘));
///可以看到每次DOM元素的样式操作都会引发重绘,如果涉及布局还会引发回流。
一. CSS中避免回流
- 尽可能在DOM树的最末端改变class
- 避免设置多层内联样式
- 动画效果应用到position属性为absolute或fixed的元素上
- 牺牲平滑度换取速度
- 避免使用table布局
- 避免使用CSS的JavaScript表达式
二. JS操作避免回流
- 避免逐项更改样式。最好一次性更改style属性,或者将样式列表定义为class并一次性更改class属性。
- 避免循环操作DOM。创建一个documentFragment或div,在它上面应用所有DOM操作,最后再把它添加到window.document。
- 也可以在一个display:none的元素上进行操作,最终把它显示出来。因为display:none上的DOM操作不会引发回流和重绘。
- 避免循环读取offsetLeft等属性。在循环之前把它们存起来。
- 绝对定位具有复杂动画的元素。绝对定位使它脱离文档刘,否则会引起父元素及后续元素大量的回流。
参考文章:回流与重绘:CSS性能让JavaScript变慢?
参考文章:浏览器的重绘(repaints)与重排(reflows)