性能瓶颈分析
- 不必要的渲染:当使用逻辑与运算符 (
&&
) 进行简洁渲染时,每次父组件的 isDataLoaded
状态变化,子组件都会重新渲染。即使子组件中的复杂 UI 渲染(列表展示、图表渲染等)在 isDataLoaded
未变化时不需要更新,也会因为父组件状态变化而重新计算渲染,浪费性能。
- 复杂计算重复执行:子组件中复杂 UI 渲染包含多个不同条件组合下的操作,如列表展示和图表渲染可能涉及数据过滤、计算等操作。每次重新渲染都会重复执行这些复杂计算,尤其是在数据量较大时,性能消耗显著。
优化方案
- 使用
React.memo
:对于子组件,使用 React.memo
包裹,它会对 props 进行浅比较。如果 props 没有变化,子组件不会重新渲染,从而避免不必要的渲染。
- shouldComponentUpdate 自定义逻辑:在类组件中,可以重写
shouldComponentUpdate
方法,根据 isDataLoaded
和其他关键数据是否变化来决定是否重新渲染。
- Memoization:对于子组件中复杂的计算逻辑(如数据过滤、计算等),可以使用
useMemo
(函数组件)或手动缓存计算结果,避免每次渲染都重复计算。
优化后的关键代码示例
函数组件使用 React.memo 和 useMemo
import React, { memo, useMemo } from'react';
// 模拟复杂数据
const data = [/* 大量数据 */];
// 子组件
const ChildComponent = memo(({ isDataLoaded }) => {
// 使用 useMemo 缓存复杂计算结果
const filteredData = useMemo(() => {
// 模拟数据过滤
return data.filter(item => item.someCondition);
}, [data]);
return (
isDataLoaded && (
<div>
{/* 基于 filteredData 进行列表展示 */}
<ul>
{filteredData.map(item => (
<li key={item.id}>{item.value}</li>
))}
</ul>
{/* 基于 filteredData 进行图表渲染,这里简单示意 */}
<div>图表渲染区域</div>
</div>
)
);
});
// 父组件
const ParentComponent = () => {
const [isDataLoaded, setIsDataLoaded] = React.useState(false);
// 模拟数据加载
React.useEffect(() => {
setTimeout(() => {
setIsDataLoaded(true);
}, 2000);
}, []);
return (
<div>
<ChildComponent isDataLoaded={isDataLoaded} />
</div>
);
};
export default ParentComponent;
类组件使用 shouldComponentUpdate
import React, { Component } from'react';
// 模拟复杂数据
const data = [/* 大量数据 */];
class ChildComponent extends Component {
shouldComponentUpdate(nextProps) {
// 仅当 isDataLoaded 变化时才重新渲染
return nextProps.isDataLoaded!== this.props.isDataLoaded;
}
render() {
const { isDataLoaded } = this.props;
// 手动缓存计算结果
let filteredData = this.filteredData;
if (!filteredData) {
filteredData = data.filter(item => item.someCondition);
this.filteredData = filteredData;
}
return (
isDataLoaded && (
<div>
{/* 基于 filteredData 进行列表展示 */}
<ul>
{filteredData.map(item => (
<li key={item.id}>{item.value}</li>
))}
</ul>
{/* 基于 filteredData 进行图表渲染,这里简单示意 */}
<div>图表渲染区域</div>
</div>
)
);
}
}
class ParentComponent extends Component {
constructor(props) {
super(props);
this.state = {
isDataLoaded: false
};
}
componentDidMount() {
setTimeout(() => {
this.setState({ isDataLoaded: true });
}, 2000);
}
render() {
const { isDataLoaded } = this.state;
return (
<div>
<ChildComponent isDataLoaded={isDataLoaded} />
</div>
);
}
}
export default ParentComponent;