react-window vs react-virtualized vs ngx-infinite-scroll vs react-infinite-scroll-component vs react-infinite-scroller vs vue-infinite-loading vs vue-virtual-scroller
前端无限滚动与虚拟滚动库技术选型指南
react-windowreact-virtualizedngx-infinite-scrollreact-infinite-scroll-componentreact-infinite-scrollervue-infinite-loadingvue-virtual-scroller类似的npm包:

前端无限滚动与虚拟滚动库技术选型指南

ngx-infinite-scrollreact-infinite-scroll-componentreact-infinite-scrollerreact-virtualizedreact-windowvue-infinite-loadingvue-virtual-scroller 是分别面向 Angular、React 和 Vue 生态的滚动增强库,用于实现高性能的列表渲染。其中,无限滚动类库(如 ngx-infinite-scrollreact-infinite-scroll-componentvue-infinite-loading)主要在用户滚动到底部时触发加载更多数据;而虚拟滚动类库(如 react-virtualizedreact-windowvue-virtual-scroller)则通过仅渲染可视区域内的元素来显著提升长列表性能。这些库适用于不同框架和场景,开发者需根据项目需求、性能目标和维护状态进行选择。

npm下载趋势

3 年

GitHub Stars 排名

统计详情

npm包名称
下载量
Stars
大小
Issues
发布时间
License
react-window4,156,02017,128209 kB11 个月前MIT
react-virtualized1,358,52527,0832.24 MB11 年前MIT
ngx-infinite-scroll293,5721,25470.3 kB153 个月前MIT
react-infinite-scroll-component03,068169 kB2013 个月前MIT
react-infinite-scroller03,30730.3 kB98-MIT
vue-infinite-loading02,657-776 年前MIT
vue-virtual-scroller010,632166 kB2514 天前MIT

无限滚动 vs 虚拟滚动:前端高性能列表渲染技术深度解析

在构建现代 Web 应用时,开发者常面临两大挑战:一是如何在用户滚动到底部时自动加载更多数据(无限滚动),二是如何高效渲染包含成千上万项的长列表(虚拟滚动)。本文将深入对比 Angular、React 和 Vue 生态中的主流解决方案,帮助你在真实项目中做出合理技术选型。

📜 核心概念区分:无限滚动 ≠ 虚拟滚动

无限滚动(Infinite Scroll) 的核心是“按需加载”——当用户滚动到容器底部时,触发网络请求获取下一页数据并追加到列表末尾。它不减少 DOM 节点数量,因此随着数据增多,页面性能会逐渐下降。

虚拟滚动(Virtual Scrolling) 的核心是“按需渲染”——只创建和维护当前可视区域内的 DOM 节点,其余节点被回收复用。无论数据量多大,DOM 节点数量始终保持恒定,从而保证流畅滚动。

⚠️ 注意:react-infinite-scroller 已在 npm 标记为 deprecated,不应在新项目中使用。

🧩 框架适配与基础用法

Angular: ngx-infinite-scroll

该库通过指令监听滚动事件,在接近底部时触发 (scrolled) 回调。

<!-- Angular 模板 -->
<div class="container"
     infiniteScroll
     [infiniteScrollDistance]="2"
     [infiniteScrollThrottle]="50"
     (scrolled)="onScroll()">
  <div *ngFor="let item of items">{{ item }}</div>
</div>
// TypeScript 组件
onScroll() {
  // 加载下一页数据
  this.loadMore();
}

React: react-infinite-scroll-component

封装为高阶组件,自动处理滚动检测和加载状态。

import InfiniteScroll from 'react-infinite-scroll-component';

function MyList() {
  return (
    <InfiniteScroll
      dataLength={items.length}
      next={fetchMoreData}
      hasMore={hasMore}
      loader={<h4>加载中...</h4>}
    >
      {items.map((item, index) => (
        <div key={index}>{item}</div>
      ))}
    </InfiniteScroll>
  );
}

React: react-window(虚拟滚动)

使用 FixedSizeList 渲染固定高度列表,极致轻量。

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

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

function App() {
  return (
    <List
      height={600}
      itemCount={10000}
      itemSize={35}
      width="100%"
    >
      {Row}
    </List>
  );
}

Vue: vue-infinite-loading

通过 <infinite-loading> 组件管理加载生命周期。

<template>
  <div class="list">
    <div v-for="item in items" :key="item.id">{{ item.name }}</div>
    <infinite-loading @infinite="infiniteHandler" />
  </div>
</template>

<script>
export default {
  methods: {
    infiniteHandler($state) {
      // 模拟异步加载
      setTimeout(() => {
        if (this.hasMore) {
          this.loadMore();
          $state.loaded();
        } else {
          $state.complete();
        }
      }, 1000);
    }
  }
};
</script>

Vue: vue-virtual-scroller

提供 <RecycleScroller> 实现高效的虚拟滚动。

<template>
  <RecycleScroller
    class="scroller"
    :items="items"
    :item-size="40"
    key-field="id"
    v-slot="{ item }"
  >
    <div>{{ item.name }}</div>
  </RecycleScroller>
</template>

<script>
import { RecycleScroller } from 'vue-virtual-scroller';
import 'vue-virtual-scroller/dist/vue-virtual-scroller.css';

export default {
  components: { RecycleScroller },
  data() {
    return {
      items: Array.from({ length: 10000 }, (_, i) => ({ id: i, name: `Item ${i}` }))
    };
  }
};
</script>

⚙️ 性能与适用场景对比

场景推荐方案原因
社交媒体动态流(每页 20 条,总数据 < 500)react-infinite-scroll-component / vue-infinite-loading / ngx-infinite-scroll数据量小,无需虚拟滚动,无限滚动足够且实现简单
聊天记录(10,000+ 条消息)react-window / vue-virtual-scroller必须使用虚拟滚动避免浏览器卡死
后台管理系统表格(1,000 行)react-window(React)或 vue-virtual-scroller(Vue)虚拟滚动保障滚动流畅性
电商商品瀑布流(图片懒加载 + 分页)react-infinite-scroll-component + 图片懒加载无限滚动配合懒加载即可满足需求

🛠️ 高级能力与限制

动态高度支持

  • react-window 默认要求固定高度。若需动态高度,需使用 react-window-dynamic-height 或改用 react-virtualizedAutoSizer + List
  • vue-virtual-scroller 内置 DynamicScroller 支持动态高度,但需提供 estimate-size 属性。
<!-- Vue 动态高度示例 -->
<DynamicScroller
  :items="items"
  :min-item-size="40"
  v-slot="{ item, index, active }"
>
  <div :style="{ height: item.height + 'px' }">{{ item.content }}</div>
</DynamicScroller>

React 虚拟滚动方案对比

react-virtualized 功能全面但体积大(>100KB),包含 Grid、Table 等复杂组件;react-window 专注核心列表/网格,体积仅 ~12KB,API 更简洁。除非需要 react-virtualized 的特定功能(如复杂的表格列宽调整),否则优先选择 react-window

Angular 与 Vue 的生态差异

Angular 官方未提供虚拟滚动方案,ngx-infinite-scroll 仅解决无限滚动问题。若需虚拟滚动,可考虑 @angular/cdk/scrolling 中的 CdkVirtualScrollViewport。Vue 生态中 vue-virtual-scroller 是成熟方案,但 Vue 3 用户应注意其兼容性,或转向 @tanstack/vue-virtual 等新库。

🔄 迁移与维护建议

  • 弃用警告react-infinite-scroller 已停止维护,现有项目应迁移到 react-infinite-scroll-component
  • 性能兜底:即使使用无限滚动,也应设置最大加载页数(如最多加载 50 页),防止内存爆炸。
  • 混合方案:对于超大数据集,可结合两者 —— 使用虚拟滚动渲染当前页,同时通过无限滚动加载新页数据(但需自行管理分页与缓存)。

💡 总结:如何选择?

  • 数据量小(< 500 项) → 用对应框架的无限滚动库(ngx-infinite-scroll / react-infinite-scroll-component / vue-infinite-loading)。
  • 数据量大(> 1000 项) → 必须用虚拟滚动(react-window / vue-virtual-scroller / Angular CDK)。
  • 需要复杂布局(表格、网格) → React 选 react-virtualized,Vue 选 vue-virtual-scrollerRecycleScroller
  • 追求轻量与现代 API → React 选 react-window,Vue 3 项目评估 @tanstack/vue-virtual

记住:没有银弹。理解你的数据规模、交互需求和性能瓶颈,才能选出最适合的工具。

如何选择: react-window vs react-virtualized vs ngx-infinite-scroll vs react-infinite-scroll-component vs react-infinite-scroller vs vue-infinite-loading vs vue-virtual-scroller

  • react-window:

    选择 react-window 如果你在 React 项目中追求极致性能和小体积的虚拟滚动方案。它由 react-virtualized 原作者开发,API 更简洁,专注于核心虚拟滚动能力。适合大多数长列表或表格场景,但不内置高级功能(如动态高度需额外处理)。是现代 React 应用的首选虚拟滚动库。

  • react-virtualized:

    选择 react-virtualized 如果你需要在 React 中实现高度可定制的虚拟滚动,支持列表、表格、网格等多种布局。它功能全面但 API 较复杂,且包体积较大。适合对滚动性能有严苛要求且愿意承担一定学习成本的项目。注意其社区活跃度已下降,新项目可优先考虑更轻量的 react-window

  • ngx-infinite-scroll:

    选择 ngx-infinite-scroll 如果你正在使用 Angular 并需要一个轻量级的无限滚动解决方案,它通过监听滚动事件触发回调,适合分页加载场景。但注意它不提供虚拟滚动能力,因此不适合渲染成千上万条数据的列表。确保你的 Angular 版本与该库兼容,避免在大型列表中使用以防止性能下降。

  • react-infinite-scroll-component:

    选择 react-infinite-scroll-component 如果你在 React 项目中需要一个简单易用的无限滚动组件,它封装了滚动检测逻辑并提供加载状态反馈。该库适合内容流式加载(如社交媒体动态),但不处理 DOM 节点复用,因此在数据量极大时仍可能造成卡顿。不要将其用于需要高性能虚拟滚动的场景。

  • react-infinite-scroller:

    选择 react-infinite-scroller 时需谨慎 —— 该库已在 npm 上标记为 deprecated(弃用),官方建议迁移到其他方案。尽管它曾提供基础的无限滚动功能,但不再维护,存在潜在 bug 和兼容性问题。新项目应避免使用,现有项目应尽快评估替换为 react-infinite-scroll-component 或虚拟滚动方案。

  • vue-infinite-loading:

    选择 vue-infinite-loading 如果你使用 Vue 2 或 Vue 3(通过兼容层)并需要一个声明式的无限滚动指令。它通过 <infinite-loading> 组件管理加载状态,集成简单,适合分页加载内容。但和所有无限滚动方案一样,它不解决大量 DOM 节点导致的性能问题,因此不适合超长列表渲染。

  • vue-virtual-scroller:

    选择 vue-virtual-scroller 如果你在 Vue 项目中需要高性能的虚拟滚动,它支持动态高度、回收机制和多种滚动模式。适用于聊天记录、联系人列表等包含数千项数据的场景。注意 Vue 3 用户需使用其官方提供的 Vue 3 兼容版本(如 @tanstack/vue-virtual 可能是更现代的替代),但原库在 Vue 2 生态中仍广泛使用。

react-window的README

react-window logo

react-window is a component library that helps render large lists of data quickly and without the performance problems that often go along with rendering a lot of data. It's used in a lot of places, from React DevTools to the Replay browser.

Support

If you like this project there are several ways to support it:

The following wonderful companies and individuals have sponsored react-window:

Installation

Begin by installing the library from NPM:

npm install react-window

TypeScript types

TypeScript definitions are included within the published dist folder

FAQs

Frequently asked questions can be found here.

Documentation

Documentation for this project is available at react-window.vercel.app; version 1.x documentation can be found at react-window-v1.vercel.app.

List

Renders data with many rows.

Required props

NameDescription
rowComponent

React component responsible for rendering a row.

This component will receive an index and style prop by default. Additionally it will receive prop values passed to rowProps.

ℹ️ The prop types for this component are exported as RowComponentProps

rowCount

Number of items to be rendered in the list.

rowHeight

Row height; the following formats are supported:

  • number of pixels (number)
  • percentage of the grid's current height (string)
  • function that returns the row height (in pixels) given an index and cellProps
  • dynamic row height cache returned by the useDynamicRowHeight hook

⚠️ Dynamic row heights are not as efficient as predetermined sizes. It's recommended to provide your own height values if they can be determined ahead of time.

rowProps

Additional props to be passed to the row-rendering component. List will automatically re-render rows when values in this object change.

⚠️ This object must not contain ariaAttributes, index, or style props.

Optional props

NameDescription
className

CSS class name.

style

Optional CSS properties. The list of rows will fill the height defined by this style.

children

Additional content to be rendered within the list (above cells). This property can be used to render things like overlays or tooltips.

defaultHeight

Default height of list for initial render. This value is important for server rendering.

listRef

Ref used to interact with this component's imperative API.

This API has imperative methods for scrolling and a getter for the outermost DOM element.

ℹ️ The useListRef and useListCallbackRef hooks are exported for convenience use in TypeScript projects.

onResize

Callback notified when the List's outermost HTMLElement resizes. This may be used to (re)scroll a row into view.

onRowsRendered

Callback notified when the range of visible rows changes.

overscanCount

How many additional rows to render outside of the visible area. This can reduce visual flickering near the edges of a list when scrolling.

tagName

Can be used to override the root HTML element rendered by the List component. The default value is "div", meaning that List renders an HTMLDivElement as its root.

⚠️ In most use cases the default ARIA roles are sufficient and this prop is not needed.

Grid

Renders data with many rows and columns.

ℹ️ Unlike List rows, Grid cell sizes must be known ahead of time. Either static sizes or something that can be derived (from the data in CellProps) without rendering.

Required props

NameDescription
cellComponent

React component responsible for rendering a cell.

This component will receive an index and style prop by default. Additionally it will receive prop values passed to cellProps.

ℹ️ The prop types for this component are exported as CellComponentProps

cellProps

Additional props to be passed to the cell-rendering component. Grid will automatically re-render cells when values in this object change.

⚠️ This object must not contain ariaAttributes, columnIndex, rowIndex, or style props.

columnCount

Number of columns to be rendered in the grid.

columnWidth

Column width; the following formats are supported:

  • number of pixels (number)
  • percentage of the grid's current width (string)
  • function that returns the column width (in pixels) given an index and cellProps
rowCount

Number of rows to be rendered in the grid.

rowHeight

Row height; the following formats are supported:

  • number of pixels (number)
  • percentage of the grid's current height (string)
  • function that returns the row height (in pixels) given an index and cellProps

Optional props

NameDescription
className

CSS class name.

dir

Indicates the directionality of grid cells.

ℹ️ See HTML dir global attribute for more information.

style

Optional CSS properties. The grid of cells will fill the height and width defined by this style.

children

Additional content to be rendered within the grid (above cells). This property can be used to render things like overlays or tooltips.

defaultHeight

Default height of grid for initial render. This value is important for server rendering.

defaultWidth

Default width of grid for initial render. This value is important for server rendering.

gridRef

Imperative Grid API.

ℹ️ The useGridRef and useGridCallbackRef hooks are exported for convenience use in TypeScript projects.

onCellsRendered

Callback notified when the range of rendered cells changes.

onResize

Callback notified when the Grid's outermost HTMLElement resizes. This may be used to (re)scroll a cell into view.

overscanCount

How many additional rows/columns to render outside of the visible area. This can reduce visual flickering near the edges of a grid when scrolling.

tagName

Can be used to override the root HTML element rendered by the List component. The default value is "div", meaning that List renders an HTMLDivElement as its root.

⚠️ In most use cases the default ARIA roles are sufficient and this prop is not needed.