一、概念
dom-tree: 浏览器把获取到的HTML代码解析成1个DOM树,DOM树里包含了所有HTML标签,包括display:none隐藏,还有用JS动态添加的元素等。
样式结构体: 浏览器把所有样式(用户定义的CSS和用户代理)解析。
render tree(渲染树): dom-tree + 样式结构体, render tree中每个NODE都有自己的style,而且render tree不包含隐藏的节点(比如display:none的节点,还有head节点),因为这些节点不会用于呈现,而且不会影响呈现的,所以就不会包含到 render tree中。
回流:当render tree中的一部分(或全部)因为元素的规模尺寸,布局,隐藏等改变而需要重新构建。这就称为回流(reflow)。每个页面至少需要一次回流,就是在页面第一次加载的时候,这时候是一定会发生回流的
例子:
- 添加或者删除可见的DOM元素;
- 元素尺寸改变——边距、填充、边框、宽度和高度
- 内容变化,比如用户在input框中输入文字
- 浏览器窗口尺寸改变——resize事件发生时
- 计算 offsetWidth 和 offsetHeight 属性
- 设置 style 属性的值
重绘:render tree中的一些元素需要更新属性,而这些属性只是影响元素的外观,风格,而不会影响布局的,比如字体color。则就叫称为重绘。
二、回流重绘区别:
- 至少会发生一次回流。
- 回流必将引起重绘,而重绘不一定会引起回流。
三、优化
可以发现回流的成本要比重绘的成本高,与构建的render-tree 节点有关系。
浏览器优化
浏览器自建一个队列,当回流和重绘达到一定数量或时间时,浏览器再更新。
代码层优化
1. 添加dom节点时,创建个模板,一次添加完,不要一个个单独添加。
2.分离读写
样式集中修改(浏览器优化机制),集中读取offsetWidth等属性(如果浏览器队列中回流等,每次读取这些属性都会引起回流),建议通过改变class或者csstext属性集中改变样式。
// 会引起四次 回流
div.style.left = '10px';
console.log(div.offsetLeft);
div.style.top = '10px';
console.log(div.offsetTop);
div.style.width = '20px';
console.log(div.offsetWidth);
div.style.height = '20px';
console.log(div.offsetHeight);
// 一次回流(前面4个样式的设置,浏览器会优化一次渲染)
div.style.left = '10px';
div.style.top = '10px';
div.style.width = '20px';
div.style.height = '20px';
console.log(div.offsetLeft);
console.log(div.offsetTop);
console.log(div.offsetWidth);
console.log(div.offsetHeight);
// 推荐使用 class 和 cssText 改变
el.className += " theclassname";
el.style.cssText += "; left: " + left + "px; top: " + top + "px;";
3.缓存布局信息
// bad 强制刷新 触发两次重排
div.style.left = div.offsetLeft + 1 + 'px';
div.style.top = div.offsetTop + 1 + 'px';
// good 缓存布局信息 相当于读写分离
var curLeft = div.offsetLeft;
var curTop = div.offsetTop;
div.style.left = curLeft + 1 + 'px';
div.style.top = curTop + 1 + 'px';
4.隐藏dom,再修改样式
dom.display = 'none'
// 修改dom样式,修改完再显示dom
dom.display = 'block'