x-note
Search…
重绘与重排
当浏览器下载完页面所有的资源后,浏览器的 HTML 解析器会开始构建 DOM Tree,同时 CSS 解析器会构建 Style Tree。合并这两个 Tree 上的信息并最终形成 Render Tree。
PS:RenderLayerTree???
Could not load image
一旦 Render Tree 构建完成,浏览器便开始绘制(paint)页面元素。

重排

当元素的几何属性发生变化,浏览器重新计算元素的几何属性的过程就称之为重排。 eg:改变了元素宽度变化,内容增加导致行数增加(高度增加)
涉及到盒模型的 CSS 属性基本会造成重排。
读取元素的某些属性(offsetLeft、offsetTop、offsetHeight、offsetWidth、scrollTop/Left/Width/Height、clientTop/Left/Width/Height、getComputedStyle()、currentStyle(in IE)); 也会造成重排

重绘

当元素发生变化浏览器将其重新绘制到屏幕上的过程称之为重绘
重排一定会引起重绘,重绘不一定伴随重排。

关于重绘与重排优化的切入点

重绘和重排操作都是代价昂贵的操作,会消耗 WEB 应用的性能。

减少盒模型操作

1
var $ele = document.querySelector(".ele");
2
3
// before
4
$ele.style.borderLeft = "1px";
5
$ele.style.borderRight = "2px";
6
$ele.style.padding = "5px";
7
// 大多数现代浏览器对上面代码进行了优化,但是老版浏览器可能导致严重的性能问题
8
9
// after
10
$ele.style.cssText = "border-left: 1px; border-right: 2px; padding: 5px;"; // 一次性修改
11
12
// or
13
$ele.className = "newClass";
Copied!

批量修改元素

基本思路:
  • 让该元素脱离文档流
  • 对其进行多重改变
  • 将元素带回文档中

隐藏元素,进行修改后,然后再显示该元素

1
function appendNode($node, data) {
2
var a, li;
3
4
for (let i = 0, max = data.length; i < max; i++) {
5
a = document.createElement("a");
6
li = document.createElement("li");
7
a.href = data[i].url;
8
9
a.appendChild(document.createTextNode(data[i].name));
10
li.appendChild(a);
11
$node.appendChild(li);
12
}
13
}
14
15
// before
16
let ul = document.querySelector("#mylist");
17
appendNode(ul, data);
18
19
// after
20
let ul = document.querySelector("#mylist");
21
ul.style.display = "none";
22
appendNode(ul, data);
23
ul.style.display = "block";
Copied!

使用文档片段创建一个子树,然后再拷贝到文档中

1
let fragment = document.createDocumentFragment();
2
appendNode(fragment, data);
3
ul.appendChild(fragment);
Copied!

将原始元素拷贝到一个独立的节点中,操作这个节点,然后覆盖原始元素

1
let old = document.querySelector("#mylist");
2
let clone = old.cloneNode(true);
3
appendNode(clone, data);
4
old.parentNode.replaceChild(clone, old);
Copied!

others

  • 移动通过 translate 来实现
  • 浏览器原理中(root 节点,css position:relative,absolute,transform,有节点溢出 overflow,alpha mask,reflection,css filter,2dCanvas,WebGl,video) 都会为这些元素创造一个新的 Layer 以便浏览器加速渲染,这些元素的修改只会涉及自己重排重绘。

Intersection Observer

Intersection Observer API 提供了一种异步观察目标元素与祖先元素或顶级文档 viewport 的交集中的变化的方法。
多种情况下都需要用到元素交集变化的信息,比如:
  • 当页面滚动时,懒加载图片或其他内容。
  • 实现 “可无限滚动” 网站,也就是当用户滚动网页时直接加载更多内容,无需翻页。
  • 为计算广告收益,检测其广告元素的曝光情况。
  • 根据用户是否已滚动到相应区域来灵活开始执行任务或动画。
获取元素的盒模型相关属性时也会导致 re-flow
1
<div id="parent" style="padding-top: 100vh;">
2
<img
3
data-src="https://www.baidu.com/img/baidu_jgylogo3.gif"
4
width="200px"
5
height="200px"
6
alt="xxx"
7
/>
8
<img
9
data-src="https://www.baidu.com/img/baidu_jgylogo3.gif"
10
width="200px"
11
height="200px"
12
style="margin-top:100vh;"
13
alt="xxx"
14
/>
15
</div>
16
<script>
17
const options = {
18
root: document,
19
rootMargin: "0px",
20
threshold: 1.0,
21
};
22
23
const observer = new IntersectionObserver((entries, observer) => {
24
if (entries.length < 1) {
25
observer.disconnect();
26
}
27
entries.forEach((entry) => {
28
if (entry.isIntersecting) {
29
const target = entry.target;
30
observer.unobserve(target);
31
if (!target.src) {
32
target.src = target.dataset.src;
33
}
34
}
35
});
36
}, options);
37
38
const $imgs = document.querySelectorAll("img");
39
$imgs.forEach((item) => {
40
observer.observe(item);
41
});
42
</script>
Copied!

Tools