加载笔记内容...
加载笔记内容...
浏览器在渲染页面的时候,大致是以下几个步骤:
回流:英文叫reflow,指的是当渲染树中的节点信息发生了大小、边距等问题,需要重新计算各节点和css具体的大小和位置。
layout(reflow)一般被称为布局,这个操作是用来计算文档中元素的位置和大小,是渲染前重要的一步。在HTML第一次被加载的时候,会有一次layout之外,js脚本的执行和样式的改变同样会导致浏览器执行layout。
重绘:英文叫repaint,当节点的部分属性发生变化,但不影响布局,只需要重新计算节点在屏幕中的绝对位置并渲染的过程,就叫重绘。比如:改变元素的背景颜色、字体颜色等操作会造成重绘。
回流的过程在重绘的过程前面,所以回流一定会重绘,但重绘不一定会引起回流。
每次回流都会对浏览器造成额外的计算消耗,所以浏览器对于回流和重绘有一定的优化机制。浏览器通常都会将多次回流操作放入一个队列中,等过了一段时间或操作达到了一定的临界值,然后才会挨个执行,这样能节省一些计算消耗。但是在获取布局信息操作的时候,会强制将队列清空,也就是强制回流,比如访问或操作以下或方法时:
这些属性或方法都需要得到最新的布局信息,所以浏览器必须去回流执行。因此,在项目中,尽量避免使用上述属性或方法,如果非要使用的时候,也尽量将值缓存起来,而不是一直获取。
减少造成回流的次数,如果要给一个节点操作多个css属性,而每一个都会造成回流的话,尽量将多次操作合并成一个
1var oDiv = document.querySelector('.box');
2oDiv.style.padding = '5px';
3oDiv.style.border = '1px solid #000';
4oDiv.style.margin = '5px';
1oDiv.style.cssText = 'padding:5px; border:1px solid #000; margin:5px;';
当对DOM有多次操作的时候,需要使用一些特殊处理减少触发回流,其实就是对 DOM 的多次操作,在脱离标准流后,对元素进行的多次操作,不会触发回流,等操作完成后,再将元素放回标准流。
脱离标准流的操作有以下3种:
display: none
createDocumentFragment
缓存选择器的结果,减少DOM查询。这里要特别提下 HTMLCollection。HTMLCollection 是通过 document.getElementByTagName
得到的对象类型,和数组类型很类似但是每次获取这个对象的一个属性,都相当于进行一次DOM查询:
1var divs = document.getElementsByTagName("div");
2for (var i = 0; i < divs.length; i++){ //infinite loop
3 document.body.appendChild(document.createElement("div"));
4}
比如上面的这段代码会导致无限循环,所以处理HTMLCollection对象的时候要做些缓存。
所有的 "getElementsBy*" 方法都会返回一个 实时的(live) 集合。这样的集合始终反映的是文档的当前状态,并且在文档发生更改时会“自动更新”。
在下面的例子中,有两个脚本。
第一个创建了对 的集合的引用。截至目前,它的长度是 1。
第二个脚本在浏览器再遇到一个 时运行,所以它的长度是 2。
1<div>First div</div>
2
3<script>
4 let divs = document.getElementsByTagName('div');
5 alert(divs.length); // 1
6</script>
7
8<div>Second div</div>
9
10<script>
11 alert(divs.length); // 2
12</script>
相反,querySelectorAll
返回的是一个 静态的 集合。就像元素的固定数组。
如果我们使用它,那么两个脚本都会输出 1
:
1<div>First div</div>
2
3<script>
4 let divs = document.querySelectorAll('div');
5 alert(divs.length); // 1
6</script>
7
8<div>Second div</div>
9
10<script>
11 alert(divs.length); // 1
12</script>
一般情况下,浏览器的layout是lazy的,也就是说:在js脚本执行时,是不会去更新DOM的,任何对DOM的修改都会被暂存在一个队列中,在当前js的执行上下文完成执行后,会根据这个队列中的修改,进行一次layout。
然而有时希望在js代码中立刻获取最新的DOM节点信息,浏览器就不得不提前执行layout,这是导致DOM性能问题的主因。
如下的操作会打破常规,并触发浏览器执行layout
部分读操作,导致 lazy reflow 失效这是导致 DOM 性能的主要原因