Report报告开发实战从零到一的那些坑与核心技术分享
优化前:卡得不行
最近在做一个报表项目,性能问题让我头疼了好一阵子。这个报表页面加载速度慢得让人抓狂,特别是数据量大的时候,简直卡得受不了。用户反馈说打开报表页面要等好几秒,体验非常糟糕。
找到瘼颈了!
首先,我得搞清楚到底是哪里出了问题。我用了一些工具来定位问题,比如Chrome的开发者工具和Lighthouse。通过这些工具,我发现以下几个主要问题:
- 大量的DOM操作导致重绘和回流
- 大量未优化的JavaScript代码
- 图片资源过大且未压缩
其中最严重的问题是DOM操作过多。每次数据更新时,整个表格都会重新渲染,这导致了大量的重绘和回流,严重影响了性能。
优化方法一:虚拟滚动
第一个优化方案是引入虚拟滚动。虚拟滚动可以减少DOM元素的数量,只渲染当前可见的部分,从而提高性能。我试了几种方案,最后发现使用react-virtualized库效果最好。
优化前的代码大概是这样的:
import React from 'react';
class Table extends React.Component {
render() {
const { data } = this.props;
return (
<table>
<thead>
<tr>
<th>列1</th>
<th>列2</th>
<th>列3</th>
</tr>
</thead>
<tbody>
{data.map((row, index) => (
<tr key={index}>
<td>{row.column1}</td>
<td>{row.column2}</td>
<td>{row.column3}</td>
</tr>
))}
</tbody>
</table>
);
}
}
优化后的代码使用了react-virtualized:
import React from 'react';
import { Table, Column, Cell } from 'react-virtualized';
class VirtualizedTable extends React.Component {
renderCell = ({ cellData, columnData }) => {
return cellData;
};
render() {
const { data } = this.props;
return (
<Table
width={800}
height={400}
headerHeight={40}
rowHeight={40}
rowCount={data.length}
rowGetter={({ index }) => data[index]}
>
<Column
label="列1"
dataKey="column1"
width={200}
cellRenderer={this.renderCell}
/>
<Column
label="列2"
dataKey="column2"
width={200}
cellRenderer={this.renderCell}
/>
<Column
label="列3"
dataKey="column3"
width={200}
cellRenderer={this.renderCell}
/>
</Table>
);
}
}
引入虚拟滚动后,表格的加载时间从5秒降到了800毫秒,效果非常明显。
优化方法二:代码优化
接下来是对JavaScript代码进行优化。我发现有些地方的代码重复计算了很多次,还有一些不必要的DOM操作。我用了React.memo和useMemo来缓存一些计算结果,减少了不必要的渲染。
优化前的代码大概长这样:
import React, { useState, useEffect } from 'react';
const Report = () => {
const [data, setData] = useState([]);
useEffect(() => {
fetchData();
}, []);
const fetchData = async () => {
const response = await fetch('https://jztheme.com/api/data');
const result = await response.json();
setData(result);
};
const calculateTotal = (data) => {
let total = 0;
for (let i = 0; i < data.length; i++) {
total += data[i].value;
}
return total;
};
return (
<div>
<h1>报表</h1>
<p>总数: {calculateTotal(data)}</p>
<table>
<thead>
<tr>
<th>列1</th>
<th>列2</th>
<th>列3</th>
</tr>
</thead>
<tbody>
{data.map((row, index) => (
<tr key={index}>
<td>{row.column1}</td>
<td>{row.column2}</td>
<td>{row.column3}</td>
</tr>
))}
</tbody>
</table>
</div>
);
};
优化后的代码如下:
import React, { useState, useEffect, useMemo } from 'react';
const Report = () => {
const [data, setData] = useState([]);
useEffect(() => {
fetchData();
}, []);
const fetchData = async () => {
const response = await fetch('https://jztheme.com/api/data');
const result = await response.json();
setData(result);
};
const calculateTotal = (data) => {
let total = 0;
for (let i = 0; i < data.length; i++) {
total += data[i].value;
}
return total;
};
const total = useMemo(() => calculateTotal(data), [data]);
return (
<div>
<h1>报表</h1>
<p>总数: {total}</p>
<table>
<thead>
<tr>
<th>列1</th>
<th>列2</th>
<th>列3</th>
</tr>
</thead>
<tbody>
{data.map((row, index) => (
<tr key={index}>
<td>{row.column1}</td>
<td>{row.column2}</td>
<td>{row.column3}</td>
</tr>
))}
</tbody>
</table>
</div>
);
};
通过使用useMemo,计算总数的操作只在数据变化时执行一次,避免了每次渲染都重新计算,性能有了显著提升。
优化方法三:图片优化
最后一个优化点是图片资源。我发现很多图片都没有经过压缩,体积非常大。我用了一些在线工具(如TinyPNG)来压缩图片,并将图片转换为WebP格式。这样不仅减小了图片的体积,还提高了加载速度。
优化前的HTML代码:
<img src="https://jztheme.com/images/report.png" alt="Report Image">
优化后的HTML代码:
<img src="https://jztheme.com/images/report.webp" alt="Report Image">
通过这些优化,页面的整体加载时间从原来的5秒缩短到了1秒左右,用户体验得到了极大的提升。
优化后:流畅多了
经过这几轮优化,报表页面的性能问题终于得到了解决。用户反馈说现在打开报表页面快了很多,不再有那种卡顿的感觉。虽然还有些小问题需要继续优化,但总体来说已经达到了预期的效果。
以上是我的优化经验,如果大家有更好的方案或者遇到类似的问题,欢迎在评论区交流。希望这篇文章能对你们有所帮助。

暂无评论