在 React 项目里,要是列表数据量很大,每次数据变化都重新渲染列表,会严重影响性能。
key
属性key
属性能帮助 React 识别哪些元素发生了变化,这样在更新列表时,React 就只更新那些真正改变的元素,而不是重新渲染整个列表。
import React from 'react';
// 假设这是我们的数据列表
const data = [
{ id: 1, name: 'Alice' },
{ id: 2, name: 'Bob' },
{ id: 3, name: 'Charlie' },
];
const ListComponent = () => {
return (
<ul>
{/* 遍历数据列表,为每个列表项添加唯一的 key */}
{data.map(item => (
<li key={item.id}>{item.name}</li>
))}
</ul>
);
};
export default ListComponent;
在这个例子中,key
属性被设置为 item.id
,因为 id
是唯一的,这样 React 就能准确知道哪些元素改变了。
React.memo
进行组件缓存React.memo
是一个高阶组件,它能对组件进行浅比较,如果组件的 props 没有变化,就不会重新渲染。
import React from 'react';
// 定义一个简单的列表项组件
const ListItem = React.memo(({ item }) => {
// 这里返回列表项的 JSX
return <li>{item.name}</li>;
});
// 假设这是我们的数据列表
const data = [
{ id: 1, name: 'Alice' },
{ id: 2, name: 'Bob' },
{ id: 3, name: 'Charlie' },
];
const ListComponent = () => {
return (
<ul>
{/* 遍历数据列表,使用 ListItem 组件 */}
{data.map(item => (
<ListItem key={item.id} item={item} />
))}
</ul>
);
};
export default ListComponent;
在这个例子中,ListItem
组件被 React.memo
包裹,当 item
props 没有变化时,这个组件就不会重新渲染。
shouldComponentUpdate
(类组件)在类组件中,可以使用 shouldComponentUpdate
生命周期方法来控制组件是否需要重新渲染。
import React from 'react';
// 定义一个类组件作为列表项
class ListItem extends React.Component {
// shouldComponentUpdate 方法,用于控制组件是否重新渲染
shouldComponentUpdate(nextProps) {
// 比较当前 props 和下一个 props 的 item 是否相同
return this.props.item.name !== nextProps.item.name;
}
render() {
// 这里返回列表项的 JSX
return <li>{this.props.item.name}</li>;
}
}
// 假设这是我们的数据列表
const data = [
{ id: 1, name: 'Alice' },
{ id: 2, name: 'Bob' },
{ id: 3, name: 'Charlie' },
];
const ListComponent = () => {
return (
<ul>
{/* 遍历数据列表,使用 ListItem 组件 */}
{data.map(item => (
<ListItem key={item.id} item={item} />
))}
</ul>
);
};
export default ListComponent;
在这个例子中,shouldComponentUpdate
方法会比较当前的 item
props 和下一个 item
props 是否相同,如果相同就不重新渲染组件。
当列表数据量非常大时,可以使用虚拟列表只渲染用户可见区域的列表项,从而提高性能。下面是一个简单的虚拟列表示例:
import React, { useState } from 'react';
// 假设这是我们的大数据列表
const bigData = Array.from({ length: 1000 }, (_, i) => ({ id: i, name: `Item ${i}` }));
const VirtualList = () => {
// 定义每个列表项的高度
const itemHeight = 30;
// 定义容器的高度
const containerHeight = 300;
// 定义显示的列表项数量
const visibleItemCount = Math.floor(containerHeight / itemHeight);
// 使用 useState 来管理滚动偏移量
const [scrollOffset, setScrollOffset] = useState(0);
// 计算开始索引
const startIndex = Math.floor(scrollOffset / itemHeight);
// 计算结束索引
const endIndex = startIndex + visibleItemCount;
// 截取可见区域的数据
const visibleData = bigData.slice(startIndex, endIndex);
return (
<div
style={{ height: containerHeight, overflowY: 'auto' }}
onScroll={(e) => setScrollOffset(e.target.scrollTop)}
>
{/* 为不可见区域预留高度 */}
<div style={{ height: startIndex * itemHeight }} />
{/* 渲染可见区域的列表项 */}
{visibleData.map(item => (
<div key={item.id} style={{ height: itemHeight }}>
{item.name}
</div>
))}
{/* 为不可见区域预留高度 */}
<div style={{ height: (bigData.length - endIndex) * itemHeight }} />
</div>
);
};
export default VirtualList;
在这个例子中,VirtualList
组件只渲染用户可见区域的列表项,当用户滚动时,动态更新可见区域的数据,这样可以显著提高性能。
通过以上这些方法,就能在 React 项目中优化列表渲染性能,避免不必要的重绘。
除了使用 key 属性,还有哪些方法可以优化 React 列表渲染性能?
在 React 里,除了使用 key
属性,还有不少方法可以优化列表渲染性能,下面详细介绍:
React.memo
是一个高阶组件,它可以对函数组件进行浅比较,若组件的 props
没有变化,就不会重新渲染该组件。
import React from 'react';
// 使用 React.memo 包裹组件,对传入的 props 进行浅比较
const ListItem = React.memo(({ item }) => {
// 渲染列表项
return <li>{item.name}</li>;
});
const data = [
{ id: 1, name: 'Alice' },
{ id: 2, name: 'Bob' },
{ id: 3, name: 'Charlie' }
];
const ListComponent = () => {
return (
<ul>
{/* 遍历数据数组,渲染列表项 */}
{data.map(item => (
<ListItem key={item.id} item={item} />
))}
</ul>
);
};
export default ListComponent;
在上述代码中,ListItem
组件被 React.memo
包裹。当 item
属性没有改变时,ListItem
组件不会重新渲染,以此减少不必要的渲染。
在类组件里,shouldComponentUpdate
生命周期方法能够控制组件是否重新渲染。你可以在这个方法里比较 props
和 state
,依据比较结果决定是否重新渲染。
import React from 'react';
class ListItem extends React.Component {
// shouldComponentUpdate 方法,用于决定组件是否重新渲染
shouldComponentUpdate(nextProps) {
// 比较当前和下一个 props 的 item 属性
return this.props.item.name!== nextProps.item.name;
}
render() {
// 渲染列表项
return <li>{this.props.item.name}</li>;
}
}
const data = [
{ id: 1, name: 'Alice' },
{ id: 2, name: 'Bob' },
{ id: 3, name: 'Charlie' }
];
const ListComponent = () => {
return (
<ul>
{/* 遍历数据数组,渲染列表项 */}
{data.map(item => (
<ListItem key={item.id} item={item} />
))}
</ul>
);
};
export default ListComponent;
在这个例子中,shouldComponentUpdate
方法会比较当前 props
和下一个 props
的 item.name
是否相同,若相同则不重新渲染组件。
React.PureComponent
和 React.Component
类似,但它自动实现了 shouldComponentUpdate
方法,会对 props
和 state
进行浅比较。
import React from 'react';
// 继承 React.PureComponent
class ListItem extends React.PureComponent {
render() {
// 渲染列表项
return <li>{this.props.item.name}</li>;
}
}
const data = [
{ id: 1, name: 'Alice' },
{ id: 2, name: 'Bob' },
{ id: 3, name: 'Charlie' }
];
const ListComponent = () => {
return (
<ul>
{/* 遍历数据数组,渲染列表项 */}
{data.map(item => (
<ListItem key={item.id} item={item} />
))}
</ul>
);
};
export default ListComponent;
在上述代码中,ListItem
组件继承自 React.PureComponent
,当 props
或 state
发生浅层次的变化时,组件才会重新渲染。
当列表数据量非常大时,可采用虚拟列表技术,只渲染用户可见区域的列表项。
import React, { useState } from 'react';
// 模拟大量数据
const bigData = Array.from({ length: 1000 }, (_, i) => ({ id: i, name: `Item ${i}` }));
const VirtualList = () => {
// 每个列表项的高度
const itemHeight = 30;
// 容器的高度
const containerHeight = 300;
// 可见列表项的数量
const visibleItemCount = Math.floor(containerHeight / itemHeight);
// 滚动偏移量状态
const [scrollOffset, setScrollOffset] = useState(0);
// 计算开始索引
const startIndex = Math.floor(scrollOffset / itemHeight);
// 计算结束索引
const endIndex = startIndex + visibleItemCount;
// 获取可见区域的数据
const visibleData = bigData.slice(startIndex, endIndex);
return (
<div
style={{ height: containerHeight, overflowY: 'auto' }}
onScroll={(e) => setScrollOffset(e.target.scrollTop)}
>
{/* 为不可见区域预留高度 */}
<div style={{ height: startIndex * itemHeight }} />
{/* 渲染可见区域的列表项 */}
{visibleData.map(item => (
<div key={item.id} style={{ height: itemHeight }}>
{item.name}
</div>
))}
{/* 为不可见区域预留高度 */}
<div style={{ height: (bigData.length - endIndex) * itemHeight }} />
</div>
);
};
export default VirtualList;
在这个例子中,VirtualList
组件仅渲染用户可见区域的列表项,当用户滚动时,动态更新可见区域的数据,避免了渲染大量不可见的列表项,从而提升了性能。