ResizeObserver防抖优化性能
提示
在工作中,有遇到过与页面回流相关的性能优化问题
# 明确定义
回流(Reflow):又称为布局,是浏览器为了重新计算页面中元素的位置和大小而进行的过程。当DOM的变化影响到元素的几何信息(如宽度、高度、边距等)时,浏览器需要重新计算元素的位置和大小,然后再进行渲染。这个过程是计算密集型的,可能会导致页面的性能问题。
重绘(Repaint):当元素的外观(颜色、背景等)发生变化,但几何属性未变时,浏览器将进行重绘。重绘不涉及元素位置的改变,因此成本较回流为低。
# 工作场景再现
项目的背景是一个数据可视化平台,左侧有一个折叠面板,可以伸缩,从展开到折叠的过程中会有动画,右侧为页面的主体,有大量的 echarts
图表。
正如之前所言,折叠面板的折叠与展开过程涉及到动画,动画过程中折叠面板的宽度不断变化,导致页面会发生不断的回流,而在这个过程中,echarts 图表也会进行同步的收缩。
这当然是一个好特性,但是这个计算密集型的过程也造成了浏览器的较大压力,会导致页面卡顿,影响了首页的使用体验。
# 检测回流次数的方法
在每一个组件内部,加入一个副作用,当检测到父容器尺寸变化时,echarts 就会进行伸缩,在这个时候,在控制台打印一次尺寸变化提醒消息。
经过检测,折叠面板折叠和展开的过程中,共发生了800+次的 echarts 图表尺寸调整,对性能造成了巨大的压力。
加入防抖后,重新检测,发现这个数字变成了100,页面的整体流畅度得到了极大的提高。
# 代码封装
注意
代码仅为示例,实际代码要与业务相结合,以下代码为ai生成,不一定可以运行。
// debounce.js
export default function debounce(func, wait) {
let timeout;
return function(...args) {
const context = this;
clearTimeout(timeout);
timeout = setTimeout(() => func.apply(context, args), wait);
}
}
1
2
3
4
5
6
7
8
9
2
3
4
5
6
7
8
9
import React, { useState, useEffect, useRef } from 'react';
import * as echarts from 'echarts/core';
import { BarChart } from 'echarts/charts';
import { CanvasRenderer } from 'echarts/renderers';
import { GridComponent, TooltipComponent } from 'echarts/components';
import debounce from './debounce.js'
echarts.use([BarChart, CanvasRenderer, GridComponent, TooltipComponent]);
const option = {
tooltip: {},
xAxis: {
data: ['衬衫', '羊毛衫', '雪纺衫', '裤子', '高跟鞋', '袜子']
},
yAxis: {},
series: [
{
name: '销量',
type: 'bar',
data: [5, 20, 36, 10, 10, 20]
}
]
};
const EChartsComponent = () => {
const chartRef = useRef(null);
const [chart, setChart] = useState(null);
// 初始化图表
useEffect(() => {
if (chartRef.current && !chart) {
const myChart = echarts.init(chartRef.current);
setChart(myChart);
}
}, [chart]);
// 防抖调整图表尺寸
const resizeChart = debounce(() => {
chart && chart.resize();
}, 300);
// 使用ResizeObserver监听容器尺寸变化
useEffect(() => {
if (chartRef.current) {
const resizeObserver = new ResizeObserver(resizeChart);
resizeObserver.observe(chartRef.current);
return () => {
resizeObserver.disconnect();
};
}
}, [chart]);
// 设置图表配置项
useEffect(() => {
if (chart) {
chart.setOption(option);
}
}, [chart]);
return <div ref={chartRef} style={{ width: '600px', height: '400px' }}></div>;
};
export default EChartsComponent;
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
54
55
56
57
58
59
60
61
62
63
64
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
54
55
56
57
58
59
60
61
62
63
64
最近更新~: 2024/08/19, 01:05:55