一、浏览器渲染的过程
解析HTML,构建DOM树。解析CSS,构建CSSOM树
DOM树和CSSOM树结合,生成渲染树
回流(Layout):根据生成的渲染树进行回流,得到节点的几何信息(大小和位置)
重绘(Painting): 根据渲染树以及已经得到的回流信息,得到节点的绝对像素。
将像素发送给GPU,展现在页面上
二、什么时候会发生回流与重绘
回流这一阶段主要是计算位置与几何信息,所以当页面布局和几何信息发生变化的时候就需要回流 。比如以下情况
页面一开始渲染的时候
浏览器的窗口发生变化(会重新计算位置和大小)
添加或删除可见的DOM元素
元素的大小(width,height,margin,padding,border-width),位置发生变化
内容发生变化(如图片的url改变)
只发生重绘的情况:改变一些只影响元素外观的样式,而不影响布局(如background改变)
三、浏览器触发回流与重绘
当浏览器获取布局信息时,由于要获取最新的布局信息,所以浏览器不得不清空队列,触发回流重绘来返回准确的值。如修改下面的值:
offset~ (offsetTop、offsetLeft、offsetWidth、offsetHeight)
scroll~ (scrollTop、scrollLeft、scrollWidth、scrollHeight)
client~ (clientTop、clientLeft、clientWidth、clientHeight)
getComputedStyle()
getBoundingClientRect
三、如何减少回流与重绘
1.合并多次对样式的修改
如:
const el = document.getElementById('test');
el.style.padding = '5px';
el.style.borderLeft = '1px';
el.style.borderRight = '2px';
可以使用cssText合并所有的改变,然后一起处理
const el = document.getElementById('test');
el.style.cssText += 'border-left: 1px; border-right: 2px; padding: 5px;';
或者通过改变className改变样式
const el = document.getElementById('test');
el.className += ' active';
2. 批量修改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);
}
}
上面代码每一次循环都添加了DOM元素,所以导致了很多次的回流与重绘
所以可以先创建完所有的元素后一次性添加
const ul = document.getElementById('list');
const fragment = document.createDocumentFragment();
appendDataToElement(fragment, data);
ul.appendChild(fragment);
3.避免触发同步布局事件
上文我们提到过,访问一些属性时会导致浏览器强制清空队列,如使用offsetWidth:
function initP() {
for (let i = 0; i < paragraphs.length; i++) {
paragraphs[i].style.width = box.offsetWidth + 'px';
}
}
上面代码每一次循环都访问了offsetWidth,所以导致了很多次的回流与重绘
所以我们要尽量减少访问这类属性
function initP() {
const width = box.offsetWidth;
for (let i = 0; i < paragraphs.length; i++) {
paragraphs[i].style.width = width + 'px';
}
}
4.对于复杂动画效果,使用绝对定位让其脱离文档流
5.css3硬件加速(GPU加速)
比起考虑如何减少回流重绘,我们更期望的是,根本不要回流重绘。
使用css3硬件加速,可以让transform、opacity、filters这些动画不会引起回流重绘 。
对于动画的其它属性,比如background-color这些,还是会引起回流重绘的,不过它还是可以提升这些动画的性能。