☰
Ant Design Blazor 表格虚拟化实战:用 EnableVirtualization 与 RemoteDataSource 驾驭十万级数据
2026/10/12 4:18:22 网站建设 项目流程
  • UI组件
  • 前端

【免费下载链接】ant-design-blazor

🌈A rich set of enterprise-class UI components based on Ant Design and Blazor.

项目地址:https://gitcode.com/gh_mirrors/an/ant-design-blazor
点击查看免费下载

导读

Ant Design Blazor 的 Table 组件在大数据量场景下,可以通过开启虚拟化机制大幅降低 DOM 渲染压力——无论是一次性渲染全部数据,还是配合按需加载模式按滚动位置动态取数,都能在保持交互流畅的同时提升页面性能。本文基于该组件库 Table 模块的虚拟化示例,讲解EnableVirtualization、ScrollY、RemoteDataSource三个核心参数的组合用法、底层实现原理与注意事项,读完即可在你的 Blazor 应用中落地两种高性能表格方案。

一、虚拟化的适用场景与核心概念

官方示例文档(virtualization.md)给出了一段简明结论:

设置EnableVirtualization与ScrollY启用虚拟化,在大数据量下提高性能(需要 .NET 5 或更高版本框架);启用RemoteDataSource即变为按需加载。

拆解开来看,表格虚拟化包含两个递进层次:

参数作用适用场景
EnableVirtualization开启虚拟化渲染,仅渲染可视区域附近的行数据已全部加载到客户端,但单页行数很多
ScrollY设置纵向滚动区域高度(像素值),为虚拟化提供固定高度的滚动容器配合虚拟化使用,是触发虚拟化的前提条件之一
RemoteDataSource切换为远程数据源模式,按需向服务端请求数据数据量极大,无法或不希望一次性加载到客户端

从源码结构看(Table.razor.cs),EnableVirtualization参数被#if NET5_0_OR_GREATER条件编译指令包裹,注释明确写着"只在 .NET 5 及以上版本生效",这与文档要求的框架版本完全一致。如果项目目标框架低于 .NET 5,该参数在编译层面就不存在。

二、全量数据 + 虚拟化:一次性加载一万行也不卡

2.1 完整示例代码

仓库中的官方演示 Virtualization.razor 提供了"Load all data"模式,先看整体结构:

<Divider Orientation="DividerOrientation.Left">Load all data</Divider> <Space> <SpaceItem> <Button OnClick="LoadData">Load data</Button> </SpaceItem> </Space> <Table TItem="Data" DataSource="data1" ScrollY="300px" EnableVirtualization HidePagination Bordered> <Selection /> <PropertyColumn Property="c=>c.Name" Width="400" /> <PropertyColumn Property="c=>c.Age" Width="150" /> <PropertyColumn Property="c=>c.Address" /> </Table>

对应代码块:

@code { class Data { public string Name { get; set; } public int Age { get; set; } public string Address { get; set; } } Data[] data1 = Array.Empty<Data>(); void LoadData() { data1 = Enumerable.Range(0, 10000).Select(i => new Data() { Name = $"Edward King {i}", Age = 32, Address = $"Edward King {i}'s address" }).ToArray(); } }

点击按钮后一次性生成并赋值 10000 条记录,表格只通过DataSource接收数据,未做任何分页,配合HidePagination隐藏分页器,所有行都"存在"于表格中,但 DOM 中实际渲染的只有可视区附近的一小部分。

2.2 关键配置解读

  • ScrollY="300px":把表格纵向滚动区高度固定为 300 像素。这是虚拟化能工作的空间基础——Blazor 内置的Virtualize组件需要在一个固定高度、可滚动的容器内测量可视范围。从 Table.razor 的模板可以看到,设置ScrollY后表格主体会渲染为ant-table-body滚动容器,样式为overflow-y: scroll且带有max-height。
  • EnableVirtualization:开启虚拟化开关(此处为布尔属性,等价于EnableVirtualization="true")。
  • HidePagination:虚拟化模式下通常与分页互斥,官方示例选择隐藏分页器。实际上从 Table.razor.cs 的实现看,只要满足虚拟化 + 远程/EF Core 数据源条件,组件会自动强制HidePagination = true。
  • Bordered:仅用于视觉效果,与虚拟化逻辑无关。
  • Selection与PropertyColumn:虚拟化对选择列和常规绑定列均正常工作,示例中Name列宽 400px、Age列宽 150px,Address列不设宽度自适应。

2.3 为什么能"快":底层的 Virtualize 组件

在 Table.razor 的表格主体渲染逻辑中,虚拟化路径实际委托给了 Blazor 内置组件:

#else if (EnableVirtualization) { return @<Microsoft.AspNetCore.Components.Web.Virtualization.Virtualize TItem="RowData<TItem>" OverscanCount="10" Items="showItems.Select((data, index) => GetRowData(data, index, level, parentRowData?.Children )).ToList()" ItemContent="bodyRow()"> </Microsoft.AspNetCore.Components.Web.Virtualization.Virtualize>; }

两点值得关注:

  1. 只渲染可视区附近的行:Virtualize组件是 Blazor 官方的虚拟化实现(位于Microsoft.AspNetCore.Components.Web.Virtualization命名空间),它根据滚动位置只渲染可视窗口内的行,因此 10000 行数据对应的 DOM 节点可能只有几十个,这是性能提升的根本原因。
  2. OverscanCount="10":组件额外预渲染可视区上下各 10 行作为缓冲区,保证快速滚动时不会出现空白闪烁。这是 Table 内部写死的值,不可通过参数调整。

另外注意,虚拟化模式下行的类型被包装为RowData<TItem>,ItemContent渲染的是bodyRow()模板,也就是说行选择、行属性、展开等既有行级能力在虚拟化路径下仍然保留。

2.4 使用注意点

  • 虚拟化要求行高可预期。如果行的实际渲染高度变化较大(例如内容换行导致高度不一),滚动定位可能出现偏差,建议控制行内内容长度。
  • EnableVirtualization与固定表头、横向滚动等能力可以并存,但需要结合实际布局验证。
  • 本示例是纯客户端数据源,虚拟化只优化渲染;如果数据量达到数十万甚至百万级,客户端内存与初始化耗时依然可观,此时应转向下面的按需加载方案。

三、远程数据源 + 虚拟化:滚动即请求的按需加载

3.1 完整示例代码

官方演示的"Load on demand"部分展示了第二种模式:

<Table TItem="Data" DataSource="data2" ScrollY="300px" EnableVirtualization OnChange="OnChange" Total="total" RemoteDataSource Bordered RowKey="x=>x.Name"> <Selection /> <PropertyColumn Property="c=>c.Name" Width="400" /> <PropertyColumn Property="c=>c.Age" Width="150" /> <PropertyColumn Property="c=>c.Address" /> </Table>

对应代码块:

@code { Data[] data2 = Array.Empty<Data>(); int total = 0; async Task OnChange(TableModels.QueryModel queryModel) { await Task.Delay(1000); data2 = Enumerable.Range(queryModel.StartIndex, queryModel.PageSize).Select(i => new Data { Name = $"Edward King {i}", Age = 32, Address = $"Edward King {i}'s address" }).ToArray(); total = 10000; } }

这个模式模拟了真实的接口调用:OnChange里await Task.Delay(1000)模拟网络延迟,随后根据QueryModel携带的StartIndex(起始下标)和PageSize(每页条数)生成对应区间的数据,并更新total为总条数 10000。

3.2 参数语义:RemoteDataSource、OnChange 与 Total 的配合

  • RemoteDataSource:声明"数据在远程",表格自身不做排序、筛选、分页计算,而是把查询条件交给OnChange回调,由你自行请求服务端。
  • OnChange:签名是EventCallback<QueryModel<TItem>>。滚动到新位置时,表格会重新触发该回调;回调中通过queryModel.StartIndex和queryModel.PageSize知道当前需要哪一段数据。
  • Total:远程模式下表格不知道总共有多少行,必须由你在数据返回后设置总条数,用于计算滚动条比例与可滚动范围。示例中在OnChange内把total = 10000。
  • RowKey="x=>x.Name":为每行指定稳定标识,虚拟化复用行容器时依赖它保持行状态正确(示例数据 Name 唯一,适合作为 Key)。

3.3 底层数据流:ItemsProvider 链路

远程 + 虚拟化模式在源码里走的是另一条路径。关键判断在 Table.razor.cs:

private bool UseItemsProvider { get { #if NET5_0_OR_GREATER return EnableVirtualization && (ServerSide || IsEntityFrameworkCore); #else return false; #endif } }

其中ServerSide定义为(Table.razor.cs):

private bool ServerSide => _hasRemoteDataSourceAttribute ? RemoteDataSource : Total > _dataSourceCount;

即:只要显式声明了RemoteDataSource,就按远程模式处理。当UseItemsProvider为真时,Table 不再使用普通渲染路径,而是通过ItemsProvider(Table.razor.cs)向Virtualize组件提供数据:

  1. Virtualize组件滚动到某个位置时,发出ItemsProviderRequest(包含StartIndex与Count);
  2. Table 据此计算本次应取的PageSize(取request.Count与剩余条数的最小值);
  3. 若数据源是IQueryable<TItem>(典型如 EF Core),直接query.Skip(StartIndex).Take(PageSize)拉取;否则调用ReloadAndInvokeChangeAsync()触发OnChange;
  4. 组装ItemsProviderResult<RowData<TItem>>返回给Virtualize,其中第二参数是_total,即滚动范围上限。

对应地,ReloadAndInvokeChange在UseItemsProvider为真时会跳过同步分页计算、只触发重渲染(Table.razor.cs),把取数职责完全交给ItemsProvider链路。这也解释了为什么示例中滚动时OnChange会被反复调用——每次滚动跨越虚拟化窗口,都是一次新的"分页请求"。

3.4 QueryModel:你与数据源之间的"分页信封"

示例中OnChange收到的TableModels.QueryModel(QueryModel.cs)携带了本次请求的完整上下文:

成员含义计算规则
PageIndex当前页码startIndex > 0时按ceil(startIndex / pageSize)推算,否则(pageIndex - 1) * pageSize
PageSize每页条数由请求直接指定
StartIndex起始下标(0 基)滚动窗口的起点
SortModel当前生效的排序条件IList<ITableSortModel>
FilterModel当前生效的筛选条件IList<ITableFilterModel>

更实用的一个能力是CurrentPagedRecords(QueryModel.cs):

public IQueryable<TItem> CurrentPagedRecords(IQueryable<TItem> query) => query.Skip(StartIndex).Take(PageSize);

如果你把DataSource传为IQueryable<TItem>(例如 EF Core 的DbSet),组件会利用该表达式直接取当前区间数据;如果你的数据来自普通 HTTP API,则需要像示例一样手动按StartIndex、PageSize组织请求参数。此外QueryModel还提供了ExecuteQuery(先排序后筛选)与GetFilterExpression(生成可给 ORM 使用的Expression<Func<TItem, bool>>),便于在服务端完成同样逻辑。

3.5 真实接口对接范式

把示例中的模拟换成真实接口,只需要改写OnChange:

async Task OnChange(TableModels.QueryModel queryModel) { // 按需组装查询参数:startIndex、pageSize,以及排序/筛选条件 var result = await Http.GetFromJsonAsync<PageResult<Data>>( $"/api/users?start={queryModel.StartIndex}&size={queryModel.PageSize}"); data2 = result.Items; total = result.Total; // 必须回填总条数 }

需要注意的是:虚拟化 + 远程模式本质上是"无限滚动 + 分段取数"的混合体,滚动位置变化会频繁触发OnChange,因此服务端接口应尽量轻量、支持游标式分页;同时建议对data2的赋值做去重/合并,避免重复请求造成状态错乱。

四、虚拟化、远程数据源与相关参数的联动关系

4.1 与 ScrollX / 固定列 / 表头的关系

  • ScrollY与ScrollX同时设置时,表格会被加上ant-table-fixed-header、ant-table-fixed-column ant-table-scroll-horizontal等样式类(Table.razor.cs),形成表头固定 + 双向滚动的布局;虚拟化行渲染位于固定高度的ant-table-body内,两种滚动互不冲突。
  • 从 Table.razor.cs 可见,只要设置了ScrollX或ScrollY,TableLayout会自动默认fixed,保证列宽不被内容撑开——这也间接保证了虚拟化场景下行高与列宽的稳定性。

4.2 与 HidePagination / PageSize 的关系

  • 官方全量示例显式设置了HidePagination;而远程模式下组件会在OnInitialized中检测UseItemsProvider并强制隐藏分页器(Table.razor.cs)。因此虚拟化模式下分页 UI 不会出现,滚动条就是你的"分页器"。
  • 全量模式下InternalReload在HidePagination且未外部指定PageSize时,会把_pageSize设为数据源总行数(Table.razor.cs),保证一次虚拟化窗口内能看到完整数据。

4.3 与 EF Core 数据源的天然契合

UseItemsProvider的判定条件还包含IsEntityFrameworkCore(数据源为IQueryable<TItem>且 Provider 字符串包含 "EntityFrameworkCore")。也就是说,即使不写RemoteDataSource,只要传入 EF Core 的IQueryable并开启EnableVirtualization,组件也会自动走ItemsProvider链路执行Skip/Take,实现服务端侧的分页取数。这是虚拟化能力在"全量数据源但数据在数据库"场景下的推荐用法。

五、两种模式选型与性能预期

维度全量数据 + 虚拟化远程数据 + 虚拟化
数据存放客户端内存(数组)服务端 / 数据库
取数方式一次性加载,滚动零请求滚动触发OnChange按段请求
关键参数EnableVirtualization+ScrollY额外加RemoteDataSource、Total、OnChange
适用规模万级以内、内存可承受十万级以上、无法全量下发
网络开销首屏一次大请求多次小请求,但首屏更快
注意事项行高稳定、避免超长内容接口需支持startIndex/size分段;回调频繁触发需幂等

需要强调的取舍:虚拟化解决的是"渲染"瓶颈,远程模式解决的是"传输 + 存储"瓶颈。若数据量在 1 万行以内且无需即时更新,全量虚拟化实现最简单、滚动最顺滑;若数据来自服务端接口且总量不可控,应优先远程按需加载,同时保证Total始终准确,否则滚动条比例会失真。

六、快速接入清单

要在你自己的项目中启用表格虚拟化,按以下步骤即可:

  1. 确认框架版本:项目目标框架必须为 .NET 5 及以上(EnableVirtualization参数受条件编译限制,见 Table.razor.cs)。
  2. 固定滚动高度:为 Table 设置ScrollY(像素值),例如ScrollY="300px"。
  3. 开启虚拟化:添加EnableVirtualization布尔参数。
  4. 选择数据模式:
    • 数据已在客户端 → 直接绑定DataSource,按需添加HidePagination;
    • 数据在服务端 → 添加RemoteDataSource,绑定OnChange在回调中按queryModel.StartIndex/queryModel.PageSize取数,并回填Total;
    • 数据源为 EF Core 的IQueryable→ 无需RemoteDataSource,开启EnableVirtualization即可自动按需Skip/Take。
  5. 验证:运行时打开浏览器开发者工具检查 DOM,滚动表格可见行节点数量基本保持恒定,即虚拟化生效。

参考资源

  • 官方虚拟化演示与说明文档:virtualization.md、Virtualization.razor
  • Table 组件参数总览(含ScrollY、ScrollX、HidePagination、OnChange等):index.zh-CN.md
  • 虚拟化与远程数据源的核心实现:Table.razor.cs、Table.razor
  • 按需取数的分页上下文模型:QueryModel.cs
  • UI组件
  • 前端

【免费下载链接】ant-design-blazor

🌈A rich set of enterprise-class UI components based on Ant Design and Blazor.

项目地址:https://gitcode.com/gh_mirrors/an/ant-design-blazor
点击查看免费下载

相关推荐

上一篇:如何免费突破百度网盘限速?Python脚本实现高速下载完整指南
下一篇:百度网盘终极下载加速方案:Python脚本实现免费高速下载

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询