You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在Angular2及以上版本实现类似md-virtual-repeat-container的虚拟滚动?

Angular 2+ 虚拟滚动实现指南

当然有不少开发者在Angular 2及以上版本里用虚拟滚动啦!我来给你梳理几种靠谱的TypeScript实现方案,完全适配Angular 2/4/5+,帮你解决找不到合适示例的问题~

一、Angular官方CDK虚拟滚动(推荐)

Angular Material的CDK(Component Dev Kit)自带了成熟的虚拟滚动模块,这是官方维护的方案,稳定性和兼容性拉满,完全能满足你的需求。

实现步骤:

  1. 先安装CDK依赖:
npm install @angular/cdk
  1. 在你的模块中导入ScrollingModule:
import { ScrollingModule } from '@angular/cdk/scrolling';

@NgModule({
  imports: [
    // ...其他业务模块
    ScrollingModule
  ]
})
export class YourFeatureModule { }
  1. 在组件模板中使用cdk-virtual-scroll-viewport:
<cdk-virtual-scroll-viewport itemSize="50" class="virtual-viewport">
  <div *cdkVirtualFor="let item of largeDataset" class="list-item">
    {{ item.name }}
  </div>
</cdk-virtual-scroll-viewport>
  • itemSize:必填项,设置单个列表项的固定高度(单位px),CDK会据此计算需要渲染的项数
  • 别忘给视口容器加固定高度样式:
.virtual-viewport {
  height: 400px;
  width: 100%;
  overflow: auto;
}

.list-item {
  height: 50px;
  padding: 0 16px;
  line-height: 50px;
  border-bottom: 1px solid #eee;
}
  1. 组件TypeScript逻辑示例:
import { Component } from '@angular/core';

@Component({
  selector: 'app-virtual-demo',
  templateUrl: './virtual-demo.component.html',
  styleUrls: ['./virtual-demo.component.css']
})
export class VirtualDemoComponent {
  // 模拟10000条数据
  largeDataset = Array.from({ length: 10000 }).map((_, index) => ({
    id: index + 1,
    name: `列表项 #${index + 1}`
  }));
}

二、第三方库实现

如果官方CDK满足不了你的特殊需求(比如支持动态高度项),可以试试这些成熟的第三方库:

  • ngx-virtual-scroller:轻量灵活,支持动态高度、横向滚动等场景
  • angular-virtual-scroll:早期流行的方案,文档完善

以ngx-virtual-scroller为例,快速实现步骤:

  1. 安装依赖:
npm install ngx-virtual-scroller
  1. 导入模块:
import { VirtualScrollerModule } from 'ngx-virtual-scroller';

@NgModule({
  imports: [VirtualScrollerModule]
})
export class YourModule { }
  1. 模板使用:
<virtual-scroller [items]="largeDataset" scrollHeight="400px" class="virtual-container">
  <ng-template let-item>
    <div class="dynamic-height-item">
      <h4>{{ item.name }}</h4>
      <p>这是一条支持动态高度的虚拟滚动项</p>
    </div>
  </ng-template>
</virtual-scroller>

三、自定义实现(适合理解原理)

如果想自己动手搞懂虚拟滚动的核心逻辑,这里给你一个简化版的自定义实现示例:

import { Component, ElementRef, HostListener, ViewChild } from '@angular/core';

@Component({
  selector: 'app-custom-virtual',
  template: `
    <div class="custom-viewport" #viewport (scroll)="handleScroll()">
      <!-- 占位容器,模拟整个列表的高度 -->
      <div class="scroll-placeholder" [style.height]="totalHeight + 'px'"></div>
      <!-- 实际渲染的可见项容器 -->
      <div class="visible-items" [style.transform]="'translateY(' + translateY + 'px)'">
        <div *ngFor="let item of visibleItems" class="list-item">{{ item.name }}</div>
      </div>
    </div>
  `,
  styles: [`
    .custom-viewport {
      height: 400px;
      overflow: auto;
      position: relative;
    }
    .scroll-placeholder {
      position: absolute;
      top: 0;
      left: 0;
      right: 0;
      z-index: -1;
    }
    .visible-items {
      position: absolute;
      top: 0;
      left: 0;
      right: 0;
    }
    .list-item {
      height: 50px;
      line-height: 50px;
      padding: 0 16px;
    }
  `]
})
export class CustomVirtualComponent {
  @ViewChild('viewport') viewportRef!: ElementRef;
  largeDataset = Array.from({ length: 10000 }).map((_, i) => ({ name: `自定义项 #${i+1}` }));
  itemHeight = 50;
  bufferCount = 5; // 前后缓冲项数,避免滚动时出现空白
  visibleItemCount = 0;
  translateY = 0;
  totalHeight = this.largeDataset.length * this.itemHeight;
  visibleItems: Array<{name: string}> = [];

  ngAfterViewInit() {
    this.calculateVisibleCount();
    this.updateVisibleItems();
  }

  calculateVisibleCount() {
    const viewportHeight = this.viewportRef.nativeElement.clientHeight;
    this.visibleItemCount = Math.ceil(viewportHeight / this.itemHeight) + this.bufferCount * 2;
  }

  handleScroll() {
    const scrollTop = this.viewportRef.nativeElement.scrollTop;
    const startIndex = Math.floor(scrollTop / this.itemHeight) - this.bufferCount;
    // 计算容器偏移量,让可见项对齐视口
    this.translateY = Math.max(startIndex, 0) * this.itemHeight;
    this.updateVisibleItems(startIndex);
  }

  updateVisibleItems(startIndex = 0) {
    const safeStart = Math.max(startIndex, 0);
    const endIndex = Math.min(safeStart + this.visibleItemCount, this.largeDataset.length);
    this.visibleItems = this.largeDataset.slice(safeStart, endIndex);
  }

  // 窗口resize时重新计算可见项数
  @HostListener('window:resize')
  onResize() {
    this.calculateVisibleCount();
    this.updateVisibleItems();
  }
}

这个自定义版本比较基础,适合学习原理,实际项目中还是推荐用官方CDK或成熟第三方库,避免自己踩兼容性和性能的坑。

内容的提问来源于stack exchange,提问作者Sunny

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.29 08:40:04