ngx-infinite-scroll、react-infinite-scroll-component、react-infinite-scroller、react-virtualized、react-window、vue-infinite-loading 和 vue-virtual-scroller 是分别面向 Angular、React 和 Vue 生态的滚动增强库,用于实现高性能的列表渲染。其中,无限滚动类库(如 ngx-infinite-scroll、react-infinite-scroll-component、vue-infinite-loading)主要在用户滚动到底部时触发加载更多数据;而虚拟滚动类库(如 react-virtualized、react-window、vue-virtual-scroller)则通过仅渲染可视区域内的元素来显著提升长列表性能。这些库适用于不同框架和场景,开发者需根据项目需求、性能目标和维护状态进行选择。
在构建现代 Web 应用时,开发者常面临两大挑战:一是如何在用户滚动到底部时自动加载更多数据(无限滚动),二是如何高效渲染包含成千上万项的长列表(虚拟滚动)。本文将深入对比 Angular、React 和 Vue 生态中的主流解决方案,帮助你在真实项目中做出合理技术选型。
无限滚动(Infinite Scroll) 的核心是“按需加载”——当用户滚动到容器底部时,触发网络请求获取下一页数据并追加到列表末尾。它不减少 DOM 节点数量,因此随着数据增多,页面性能会逐渐下降。
虚拟滚动(Virtual Scrolling) 的核心是“按需渲染”——只创建和维护当前可视区域内的 DOM 节点,其余节点被回收复用。无论数据量多大,DOM 节点数量始终保持恒定,从而保证流畅滚动。
⚠️ 注意:
react-infinite-scroller已在 npm 标记为 deprecated,不应在新项目中使用。
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-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-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-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-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-virtualized 的 AutoSizer + 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-virtualized 功能全面但体积大(>100KB),包含 Grid、Table 等复杂组件;react-window 专注核心列表/网格,体积仅 ~12KB,API 更简洁。除非需要 react-virtualized 的特定功能(如复杂的表格列宽调整),否则优先选择 react-window。
Angular 官方未提供虚拟滚动方案,ngx-infinite-scroll 仅解决无限滚动问题。若需虚拟滚动,可考虑 @angular/cdk/scrolling 中的 CdkVirtualScrollViewport。Vue 生态中 vue-virtual-scroller 是成熟方案,但 Vue 3 用户应注意其兼容性,或转向 @tanstack/vue-virtual 等新库。
react-infinite-scroller 已停止维护,现有项目应迁移到 react-infinite-scroll-component。ngx-infinite-scroll / react-infinite-scroll-component / vue-infinite-loading)。react-window / vue-virtual-scroller / Angular CDK)。react-virtualized,Vue 选 vue-virtual-scroller 的 RecycleScroller。react-window,Vue 3 项目评估 @tanstack/vue-virtual。记住:没有银弹。理解你的数据规模、交互需求和性能瓶颈,才能选出最适合的工具。
选择 react-window 如果你在 React 项目中追求极致性能和小体积的虚拟滚动方案。它由 react-virtualized 原作者开发,API 更简洁,专注于核心虚拟滚动能力。适合大多数长列表或表格场景,但不内置高级功能(如动态高度需额外处理)。是现代 React 应用的首选虚拟滚动库。
选择 react-virtualized 如果你需要在 React 中实现高度可定制的虚拟滚动,支持列表、表格、网格等多种布局。它功能全面但 API 较复杂,且包体积较大。适合对滚动性能有严苛要求且愿意承担一定学习成本的项目。注意其社区活跃度已下降,新项目可优先考虑更轻量的 react-window。
选择 ngx-infinite-scroll 如果你正在使用 Angular 并需要一个轻量级的无限滚动解决方案,它通过监听滚动事件触发回调,适合分页加载场景。但注意它不提供虚拟滚动能力,因此不适合渲染成千上万条数据的列表。确保你的 Angular 版本与该库兼容,避免在大型列表中使用以防止性能下降。
选择 react-infinite-scroll-component 如果你在 React 项目中需要一个简单易用的无限滚动组件,它封装了滚动检测逻辑并提供加载状态反馈。该库适合内容流式加载(如社交媒体动态),但不处理 DOM 节点复用,因此在数据量极大时仍可能造成卡顿。不要将其用于需要高性能虚拟滚动的场景。
选择 react-infinite-scroller 时需谨慎 —— 该库已在 npm 上标记为 deprecated(弃用),官方建议迁移到其他方案。尽管它曾提供基础的无限滚动功能,但不再维护,存在潜在 bug 和兼容性问题。新项目应避免使用,现有项目应尽快评估替换为 react-infinite-scroll-component 或虚拟滚动方案。
选择 vue-infinite-loading 如果你使用 Vue 2 或 Vue 3(通过兼容层)并需要一个声明式的无限滚动指令。它通过 <infinite-loading> 组件管理加载状态,集成简单,适合分页加载内容。但和所有无限滚动方案一样,它不解决大量 DOM 节点导致的性能问题,因此不适合超长列表渲染。
选择 vue-virtual-scroller 如果你在 Vue 项目中需要高性能的虚拟滚动,它支持动态高度、回收机制和多种滚动模式。适用于聊天记录、联系人列表等包含数千项数据的场景。注意 Vue 3 用户需使用其官方提供的 Vue 3 兼容版本(如 @tanstack/vue-virtual 可能是更现代的替代),但原库在 Vue 2 生态中仍广泛使用。
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.
If you like this project there are several ways to support it:
The following wonderful companies and individuals have sponsored react-window:
Begin by installing the library from NPM:
npm install react-window
TypeScript definitions are included within the published dist folder
Frequently asked questions can be found here.
Documentation for this project is available at react-window.vercel.app; version 1.x documentation can be found at react-window-v1.vercel.app.
Renders data with many rows.
| Name | Description |
|---|---|
| rowComponent | React component responsible for rendering a row. This component will receive an ℹ️ The prop types for this component are exported as |
| rowCount | Number of items to be rendered in the list. |
| rowHeight | Row height; the following formats are supported:
⚠️ 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 |
| Name | Description |
|---|---|
| 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 |
| 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. |
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.
| Name | Description |
|---|---|
| cellComponent | React component responsible for rendering a cell. This component will receive an ℹ️ The prop types for this component are exported as |
| 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 |
| columnCount | Number of columns to be rendered in the grid. |
| columnWidth | Column width; the following formats are supported:
|
| rowCount | Number of rows to be rendered in the grid. |
| rowHeight | Row height; the following formats are supported:
|
| Name | Description |
|---|---|
| className | CSS class name. |
| dir | Indicates the directionality of grid cells. ℹ️ See HTML |
| 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 |
| 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. |