在浏览器中HTML、CSS和JavaScript的使用都会使页面进行「解析」和「加载」。
解析
浏览器在解析HTML的时候会将整个文档解析为一个DOM树,也就是DOMTree。DOMTree遵循「深度优先原则」,比如下面👇的文档结构。
<!DOCTYPE html><html lang="en"><head><meta charset="UTF-8" /><meta http-equiv="X-UA-Compatible" content="IE=edge" /><meta name="viewport" content="width=device-width, initial-scale=1.0" /><title>Document</title></head><body><div><ul><li><a href=""></a><span style="display: none"></span><img src="" alt="" /></li></ul></div></body></html><script>// TODO</script>

同样的,CSS在解析的时候也会创建一个样式结构树,也就是CSSTree。
最后DOMTree+CSSTree结合形成renderTree也就是渲染树,然后浏览器才用renderTree开始渲染页面。
:::info
renderTree的渲染过程:
- 渲染树每个节点都有自己的样式
- 渲染树不包含隐藏节点(
**display: none;**) **visibility: hidden;**是会进行页面绘制的,只是页面不可见- 渲染树上的每个节点都会被当作是一个盒子
:::


加载
DOMTree在渲染期间是不会进行加载操作的(先有解析后又加载,解析和加载是异步完成的)。
解析只是把img放到DOMTree上,然后才开始加载资源。
回流(重排)/重绘
:::info
回流(重排):当使用JavaScript对页面上的节点进行大小、布局、显示隐藏操作时就会产生「回流」和「重绘」。
重绘:除开「重排」外对节点进行更改背景色、字体色等就会引起重绘。
⚠️ 回流一定会引起重绘,而重绘不一定会引起回流(也就是说重绘可以单独产生),回流比重绘的代价要大的多!!!
一个页面至少有一次回流+重绘(也就是初始化的时候)。
回流时浏览器会重新构建受影响的部分(或全部)renderTree,这个时候一定会引起重绘。
:::
下面详细的说明会引起回流的操作:
- 对
DOM的增删 - 对
DOM节点的位置变化 - 对元素
margin、padding、width、height、border的更改 - 元素设置
display: block/none;(visibility: hidden;会引起重绘) - 页面初始化渲染
- 浏览器窗口尺寸的变化(缩放浏览器窗口)
- 操作元素
offset、scroll、client、getComputedStyle
除开以上都会引起重绘。
如何减少回流+重绘
假如我们用JS操作元素的样式:
var oBox = document.getElementById("box");var boxStyle = oBox.style;boxStyle.width = "100px"; // 回流+重绘boxStyle.height = "100px"; // 回流+重绘boxStyle.margin = "10px"; // 回流+重绘boxStyle.padding = "10px"; // 回流+重绘boxStyle.backgroundColor = "black"; // 重绘boxStyle.border = "1px solid red"; // 回流+重绘boxStyle.color = "#fff"; // 重绘boxStyle.fontSize = "32px"; // 回流+重绘var h1 = document.createElement("h1");h1.innerHTML = "我是一个h1";// appendChild 只会重绘+回流 h1 自己,如果插入到某元素之前,那么 h1 后面所有的元素都需要回流+重绘document.body.appendChild(h1);
以上代码可以看到当我们频繁的给oBox设置样式就会频繁的引起回流和重绘,那么如何进行优化呢?
1、利用display: none;的属性
var oBox = document.getElementById("box");var boxStyle = oBox.style;boxStyle.display = "none"; // 回流+重绘boxStyle.width = "100px";boxStyle.height = "100px";boxStyle.margin = "10px";boxStyle.padding = "10px";boxStyle.backgroundColor = "black";boxStyle.border = "1px solid red";boxStyle.color = "#fff";boxStyle.fontSize = "32px";boxStyle.display = "block"; // 回流+重绘
2、给元素设置类名
// 在 css 中定义好 .active// .active{...}var oBox = document.getElementById("box");oBox.className += " active"; // 这样只会引起一次回流+重绘
3、利用style.cssText属性
这样的方式适用于动态值。
var oBox = document.getElementById("box");oBox.style.cssText = `width: ${200}px; height: 100px; background-color: green`;
4、创建文档碎片
var frag = document.createDocumentFragment();for (let i = 0; i < 10; i++) {frag.appendChild(document.createElement("li"));}document.body.appendChild(frag);
5、缓存数据
var oBox = document.getElementById("box");var offset = oBox.offsetWidth;div.style.width = offset + 10 + "px";
