海量数据页面渲染优化

  目录

页面有大量数据渲染的优化处理

海量数据页面渲染优化

平时在工作中需要在页面中渲染的数据量并不多,不过在极端情况下可能会渲染成千上万条,如果直接把数据生成dom节点往页面中插入的话,可能会使浏览器挂掉,下面列举3个优化方式:

一 使用requestAnimationFrame

使用requestAnimationFrame函数,可以根据浏览器刷新频率进行渲染。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<div>
<ul id="ul"></ul>
</div>
</body>
<script>
const maxNum = 50000;
const num = 20;
let count = 0;
let ul = document.querySelector('#ul');
function add() {
let frag = document.createDocumentFragment();
for(let j=0; j<num; j++) {
let li = document.createElement('li');
li.innerHTML = num*count + j;
frag.appendChild(li);
}
ul.appendChild(frag);
loop();
}
function loop() {
count++;
if(count < maxNum/num) {
requestAnimationFrame(add);
}else {
console.log('总用时->'+Math.ceil((performance.now() - startTime))+'ms');
}
}
let startTime = performance.now();
add();
</script>
</html>

二 使用setTimeout

使用setTimeout函数,完成一段工作后将线程让出去,让浏览器其它任务执行

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<div>
<ul id="ul"></ul>
</div>
</body>
<script>
const maxNum = 50000;
const num = 20;
let count = 0;
let ul = document.querySelector('#ul');
function add() {
let frag = document.createDocumentFragment();
for(let j=0; j<num; j++) {
let li = document.createElement('li');
li.innerHTML = num*count + j;
frag.appendChild(li);
}
ul.appendChild(frag);
loop();
}
function loop() {
count++;
if(count < maxNum/num) {
setTimeout(add);
}else {
console.log('总用时->'+Math.ceil((performance.now() - startTime))+'ms');
}
}
let startTime = performance.now();
add();
</script>
</html>

上述两种方式,一个是使用requestAnimationFrame,一个是使用setTimeout,前者是根据浏览器刷新频率,60HZ就是1000/60毫秒执行一次,但这里有个问题我没有实验,就是单词任务如果超过一次刷新的时间会如何处理呢?(所以尽可能把单次任务执行时间控制在浏览器一次刷新时间范围内)。
而setTimeout就没有刷新时间,任务执行完成之后,任务队列中没有其它任务的话,直接执行setTimeout里的任务。
所以在都是50000条数据,单次向dom中插入20条数据的任务,两者所耗费的时间是不一样的。
方法一用时:总用时->168309ms
方法二用时: 总用时->94812ms
根据用时可以看出来,方法二不需要等待浏览器的刷新时间(在理想状态,没有其它任务情况下),所需时间是比方法一少的。

三 时间切片

什么是时间切片

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<div>
<ul id="ul"></ul>
</div>
</body>
<script>
const maxNum = 50000;
const num = 20;
let count = 0;
let ul = document.querySelector('#ul');

function *gen() {
while(count < maxNum/num) {
yield (function() {
let frag = document.createDocumentFragment();
for(let j=0; j<num; j++) {
let li = document.createElement('li');
li.innerHTML = num*count + j;
frag.appendChild(li);
}
ul.appendChild(frag);
})();
}
}
function ts(gen) {
if (typeof gen === 'function') gen = gen()
if (!gen || typeof gen.next !== 'function') return
return function next() {
const start = performance.now()
let res = null
do {
res = gen.next();
count++;
} while (!res.done && performance.now() - start < 30)

if (res.done) {
console.log('总用时->'+Math.ceil((performance.now() - startTime))+'ms');
return
}
setTimeout(next)
}
}
let startTime = performance.now();
ts(gen)();
</script>
</html>

方法三用时: 总用时->1703ms
可以看出速度明显高于前两种方法,因为方法三可以设置切片时间,根据浏览器的性能或者页面的实际交互情况来设置具体的切片时间。