以上属性和方法都需要返回最新的布局信息,因此浏览器不得不清空队列,触发回流重绘来返回正确的值。因此,我们在修改样式的时候,最好避免使用上面列出的属性,他们都会刷新渲染队列。如果要使用它们,最好将值缓存起来。
减少回流和重绘
好了,到了我们今天的重头戏,前面说了这么多背景和理论知识,接下来让我们谈谈如何减少回流和重绘。
最小化重绘和重排
由于重绘和重排可能代价比较昂贵,因此最好就是可以减少它的发生次数。为了减少发生次数,我们可以合并多次对DOM和样式的修改,然后一次处理掉。考虑这个例子
- const el = document.getElementById('test');
- el.style.padding = '5px';
- el.style.borderLeft = '1px';
- el.style.borderRight = '2px';
例子中,有三个样式属性被修改了,每一个都会影响元素的几何结构,引起回流。当然,大部分现代浏览器都对其做了优化,因此,只会触发一次重排。但是如果在旧版的浏览器或者在上面代码执行的时候,有其他代码访问了布局信息(上文中的会触发回流的布局信息),那么就会导致三次重排。
因此,我们可以合并所有的改变然后依次处理,比如我们可以采取以下的方式:
- const el = document.getElementById('test');
- el.style.cssText += 'border-left: 1px; border-right: 2px; padding: 5px;';
- const el = document.getElementById('test');
- el.className += ' active';
批量修改DOM
当我们需要对DOM对一系列修改的时候,可以通过以下步骤减少回流重绘次数:
- 使元素脱离文档流
- 对其进行多次修改
- 将元素带回到文档中。
该过程的第一步和第三步可能会引起回流,但是经过第一步之后,对DOM的所有修改都不会引起回流重绘,因为它已经不在渲染树了。
有三种方式可以让DOM脱离文档流:
- 隐藏元素,应用修改,重新显示
- 使用文档片段(document fragment)在当前DOM之外构建一个子树,再把它拷贝回文档。
- 将原始元素拷贝到一个脱离文档的节点中,修改节点后,再替换原始的元素。
考虑我们要执行一段批量插入节点的代码:
- function appendDataToElement(appendToElement, data) {
- let li;
- for (let i = 0; i < data.length; i++) {
- li = document.createElement('li');
- li.textContent = 'text';
- appendToElement.appendChild(li);
- }
- }
- const ul = document.getElementById('list');
- appendDataToElement(ul, data);
如果我们直接这样执行的话,由于每次循环都会插入一个新的节点,会导致浏览器回流一次。
我们可以使用这三种方式进行优化:
隐藏元素,应用修改,重新显示
这个会在展示和隐藏节点的时候,产生两次回流
- function appendDataToElement(appendToElement, data) {
- let li;
- for (let i = 0; i < data.length; i++) {
- li = document.createElement('li');
- li.textContent = 'text';
- appendToElement.appendChild(li);
- }
- }
- const ul = document.getElementById('list');
- ul.style.display = 'none';
- appendDataToElement(ul, data);
- ul.style.display = 'block';
使用文档片段(document fragment)在当前DOM之外构建一个子树,再把它拷贝回文档
- const ul = document.getElementById('list');
- const fragment = document.createDocumentFragment();
- appendDataToElement(fragment, data);
- ul.appendChild(fragment);
(编辑:ASP站长网)
|