用react-window优化长列表性能的实战经验分享

Dev · 朱莉 优化 阅读 1,944
赞 14 收藏
二维码
手机扫码查看
反馈

先看效果,再看代码

最近在做一个项目,里面有个需求是展示一个超长的列表,几千条数据那种。直接用普通的 <ul> 渲染?开玩笑,页面直接卡死。于是我翻出了 react-window,亲测有效,性能杠杠的。

用react-window优化长列表性能的实战经验分享

简单来说,react-window 是一个用于渲染大型列表的 React 库,它通过只渲染可见区域的内容来提升性能。废话不多说,先上代码:

import React from 'react';
import { FixedSizeList as List } from 'react-window';

const Row = ({ index, style }) => (
  <div style={style}>Row {index}</div>
);

const App = () => (
  <List
    height={500}
    itemCount={1000}
    itemSize={35}
    width={300}
  >
    {Row}
  </List>
);

export default App;

这段代码的效果是一个高度为 500px、宽度为 300px 的容器,里面有 1000 条数据,但只会渲染当前可见区域的几条。运行一下,你会发现滚动非常流畅,内存占用也很低。

这个场景最好用

我遇到过一个实际场景:后台管理系统需要展示用户的日志记录,数据量动辄上万条。如果用传统的 map 遍历渲染,别说用户体验了,开发者自己看着都难受。

react-window 在这种场景下简直完美。尤其是固定高度的列表,比如日志记录、订单列表、聊天记录等。代码稍微改改就能用:

import React from 'react';
import { FixedSizeList as List } from 'react-window';

const LogItem = ({ index, style }) => (
  <div style={{ ...style, padding: '10px', borderBottom: '1px solid #ccc' }}>
    Log Entry {index + 1}: Some log content here
  </div>
);

const LogViewer = () => (
  <List
    height={600}
    itemCount={10000}
    itemSize={50}
    width={'100%'}
  >
    {LogItem}
  </List>
);

export default LogViewer;

这里我把 width 设置为 '100%',让列表自适应父容器宽度。亲测有效,尤其适合那种全屏展示的后台界面。

踩坑提醒:这三点一定注意

虽然 react-window 很好用,但也有一些坑点需要注意:

  • 动态高度的坑:如果你的列表项高度不固定,那就不能用 FixedSizeList,而是要用 VariableSizeList。但是,动态高度的性能会差一些,建议尽量避免。
  • 样式问题:列表项的样式需要手动设置,比如 paddingborder,否则可能会出现对齐问题。我在一个项目里折腾了半天才发现是因为没加 box-sizing: border-box;
  • 滚动事件失效:如果在列表外面套了一个自定义滚动容器,记得给 List 组件传递正确的 scrollOffset 或者监听滚动事件,否则滚动可能会失效。

举个例子,动态高度的实现方式如下:

import React from 'react';
import { VariableSizeList as List } from 'react-window';

const getItemSize = (index) => (index % 2 === 0 ? 50 : 100);

const Row = ({ index, style }) => (
  <div style={style}>Row {index} with dynamic height</div>
);

const App = () => {
  const listRef = React.useRef(null);

  return (
    <List
      height={500}
      itemCount={1000}
      itemSize={(index) => getItemSize(index)}
      width={300}
      ref={listRef}
    >
      {Row}
    </List>
  );
};

export default App;

这里通过 itemSize 函数动态计算每一行的高度,虽然灵活,但性能确实不如固定高度。

高级技巧:嵌套滚动和自定义内容

有时候我们需要在一个页面中嵌套多个滚动区域,或者在列表项中加入复杂的自定义内容,比如图片、按钮、甚至是图表。

这里分享一个我用过的高级技巧:嵌套滚动。假设我们有一个左右布局的页面,左边是一个固定高度的导航栏,右边是一个可滚动的列表:

javascript
import React from 'react';
import { FixedSizeList as List } from 'react-window';

const Sidebar = () => (
<div style={{ width: 200, backgroundColor: '#f0f0f0', padding: '10px' }}>
Sidebar Content
</div>
);

const Row = ({ index, style }) => (
<div style={style}>
<img
src={
https://jztheme.com/images/sample-${index % 10}.jpg}
alt={
Sample ${index}}
style={{ width: '100%', height: 'auto' }}
/>
<p>Row {index}</p>
</div>
);

const App = () => (
<div style={{ display: 'flex', height: '100vh' }}>
<Sidebar />
<div style={{ flex: 1, overflow: 'hidden' }}>
<List
height={window.innerHeight}
itemCount={1000}
itemSize={200}
width={'100%'}
>
{Row}
</List>
</div>
</div>
);

export default App;
`>

这里我把 List 的高度设置为 window.innerHeight,让它填满右侧区域。每个列表项包含一张图片和一段文字,滚动依然很流畅。

拓展用法:更多可能性

react-window 还有很多拓展用法,比如结合虚拟化表格、无限滚动加载、甚至是动画效果。这些功能我还在摸索中,后续会继续分享。

以上是我个人对 react-window 的完整讲解,有更优的实现方式欢迎评论区交流。这个技巧的拓展用法还有很多,后续会继续分享这类博客。

本文章不代表JZTHEME立场,仅为作者个人观点 / 研究心得 / 经验分享,旨在交流探讨,供读者参考。
发表评论

暂无评论