- 前端
- UI组件
- 设计系统
【免费下载链接】ant-design-blazor
基于 Ant Design 与 Blazor 的前端组件库。让开发者解放生产力,实现更大价值。
在 Ant Design Blazor 组件库中,当 Select 选择器需要承载上千甚至数千个选项时,如果将所有选项一次性渲染进下拉列表 DOM,会显著拖慢首屏渲染、交互响应和内存占用。针对这一场景,Select 组件 提供了EnableVirtualization参数,开启后利用 Blazor 内置的Virtualize组件按需渲染可视区内的选项。本文围绕该参数的原理、用法、配套参数与适用边界展开,帮助你为大数据量选择器写出高性能的实战方案。
一、为什么大数据量选项需要虚拟化
1.1 常规渲染模式下的性能瓶颈
在不开启虚拟化时,Select 的下拉列表会通过 ForeachLoop 将SortedSelectOptionItems中的每一个选项完整渲染为SelectOption组件。ForeachLoop 的实现非常直观:遍历Items集合,为每一项调用ChildContent(item)构建渲染树:
protected override void BuildRenderTree(RenderTreeBuilder builder) { foreach (var item in Items) { ChildContent(item)(builder); } }这种"全量渲染"的代价是显而易见的:当选项数量达到数千级别时,每一个选项都要生成对应的组件实例、参与 DIFF 比对并持有 DOM 节点,下拉面板的打开、滚动和搜索都会因为 DOM 节点过多而卡顿。因此对于几十、上百个选项的场景,全量渲染没有问题;一旦规模上升到千级,就必须引入虚拟化。
1.2 虚拟化的核心思路
虚拟化的本质是"只渲染视口内可见的那部分选项"。Select 组件复用 Blazor 框架自带的Microsoft.AspNetCore.Components.Web.Virtualization.Virtualize<TItem>组件:它感知滚动容器的可视区域高度与滚动位置,动态计算当前需要渲染的项,并在滚动过程中回收不可见项的 DOM,从而把 DOM 节点数量控制在一个固定的小规模范围内。
二、开启虚拟化:EnableVirtualization 参数
2.1 参数定义与版本要求
EnableVirtualization是Select<TItemValue, TItem>上的一个布尔类型参数,位于 components/select/Select.razor.cs:
#if NET5_0_OR_GREATER /// <summary> /// Whether to enable virtualization feature or not, only works for .NET 5 and higher /// </summary> /// <default value="false"/> [Parameter] public bool EnableVirtualization { get; set; } #endif三个关键信息:
- 默认值为
false,即默认不开启虚拟化; - 参数被
#if NET5_0_OR_GREATER条件编译指令包裹,该特性仅对 .NET 5 及以上版本生效,这与关联文档中"需要用 .NET 5 以上"的说明完全一致; - 在 .NET 5 以下的运行时中,该参数不参与编译,等价于始终关闭。
2.2 渲染分支的实现
开启虚拟化后,Select 在下拉列表的渲染路径上会切换到Virtualize分支。核心逻辑位于 components/select/Select.razor:
RenderFragment SelectOptionsRender() { #if NET5_0_OR_GREATER if(EnableVirtualization) { return @<Microsoft.AspNetCore.Components.Web.Virtualization.Virtualize Items=@SortedSelectOptionItems.Where(x => !x.IsHidden).ToList() ChildContent="optionRender(ItemTemplate)"/>; } #endif return @<ForeachLoop Items=@SortedSelectOptionItems ChildContent="optionRender(ItemTemplate)" />; }这段实现可以解读出几个重要细节:
- 先排序后过滤再虚拟化:传入
Virtualize的Items是SortedSelectOptionItems(已按SortByGroup/SortByLabel排序后的选项序列)过滤掉IsHidden项之后的列表。排序逻辑定义在 SelectBase.razor.cs,会根据组名、标签名的升降序组合对选项重排; - 隐藏项不参与虚拟化渲染:
Where(x => !x.IsHidden)保证被隐藏的选项(例如HideSelected场景下已选中的项)不会占用虚拟化窗口的渲染名额; - 渲染内容保持一致:无论是
Virtualize分支还是ForeachLoop分支,最终都通过同一个optionRender委托渲染SelectOption,因此开启虚拟化不会改变选项的外观与行为,只是改变了渲染时机与数量。
另外需要注意:在 Select.razor 的渲染逻辑中,虚拟化分支只在未启用分组(IsGroupingEnabled为 false)时走SelectOptionsRender();当设置了GroupName启用分组展示时,会走SelectOptionGroup的渲染路径,因此分组模式与虚拟化并不叠加,大数据量场景应避免与分组功能混用。
三、官方 Demo 实战:2000 条数据的完整示例
3.1 Demo 文件位置与整体结构
官方示例位于 site/AntDesign.Docs/Demos/Components/Select/demo/BigData.razor,对应的文档说明为 big-data.md。示例同时演示了多选(multiple)与单选(default)两种模式下的虚拟化用法,并在页面顶部用Title实时展示当前选项总数。
3.2 完整代码与逐项拆解
<Title Level="4">@_options.Count() Items</Title> <Select Mode="SelectMode.Multiple" Placeholder="Please select" DataSource="@_options" @bind-Values="@_selectedValues" LabelName="@nameof(LabeledValue.Label)" ValueName="@nameof(LabeledValue.Value)" DisabledName="@nameof(LabeledValue.Disabled)" TItemValue="string" TItem="LabeledValue" OnSelectedItemsChanged="OnSelectedItemsChangedHandler" EnableSearch EnableVirtualization> </Select> <Select Mode="default" Placeholder="Please select" DataSource="@_options" @bind-Value="@_selectedValue" LabelName="@nameof(LabeledValue.Label)" ValueName="@nameof(LabeledValue.Value)" DisabledName="@nameof(LabeledValue.Disabled)" TItemValue="string" TItem="LabeledValue" OnSelectedItemChanged="OnSelectedItemChangedHandler" EnableSearch EnableVirtualization Style="width: 100%; margin-top: 8px;"> </Select>@code { class LabeledValue { public LabeledValue () {} public LabeledValue (string value, string label, bool disabled) { Value = value; Label = label; Disabled = disabled; } public string Value { get; set; } public string Label { get; set; } public bool Disabled { get; set; } } List<LabeledValue> _options; IEnumerable<string> _selectedValues = new List<string> { "0a10", "0c12" }; string _selectedValue = "000"; protected override void OnInitialized() { const int min = 0; const int max = 2000; _options = new List<LabeledValue>(); for (var i = min; max > i; i++) { var disabled = i == 10; var value = Convert.ToString(i, 16).PadLeft(2, '0') + i.ToString(); _options.Add(new LabeledValue(value, value, disabled)); } } private void OnSelectedItemsChangedHandler(IEnumerable<LabeledValue> values) { Console.WriteLine($"selected: ${string.Join(",", values.Select(x => x.Label))}"); } private void OnSelectedItemChangedHandler(LabeledValue value) { Console.WriteLine($"selected: ${value.Label}"); } }这个 Demo 完整演示了大数据量场景的标准用法,关键点如下:
- 数据量:
OnInitialized中循环生成 2000 条LabeledValue数据,模拟真实的大数据源。选项的值由十六进制前缀与索引拼接而成(如"0a10"),保证 2000 条数据中每条的 Value 唯一; - 数据源绑定:通过
DataSource="@_options"传入列表,并使用LabelName、ValueName、DisabledName三个字符串参数声明数据模型中充当标签、值、禁用标识的属性名——这些参数在 Select.razor.cs 中通过PathHelper.GetDelegate编译为属性访问委托,是DataSource模式下描述数据模型的标准方式; - 泛型声明:
TItemValue="string"、TItem="LabeledValue"显式指定了值类型与数据项类型; - 单选与多选:第一个 Select 使用
Mode="SelectMode.Multiple"并通过@bind-Values双向绑定多选值集合,初始选中"0a10"与"0c12";第二个 Select 使用默认单选模式,通过@bind-Value双向绑定单个值,初始选中"000"; - 虚拟化与搜索:两个 Select 都同时开启了
EnableVirtualization与EnableSearch,说明大数据场景下虚拟化与搜索功能可以协同工作; - 禁用项示例:
var disabled = i == 10;将第 10 条数据标记为禁用,验证了大数据量下DisabledName/禁用状态仍然正常工作; - 回调事件:单选使用
OnSelectedItemChanged,多选使用OnSelectedItemsChanged,在控制台输出当前选中项的 Label,便于观察选中变化。
3.3 Demo 文档说明
big-data.md的中文说明为:"当需要展示大量数据时,可以设置EnableVirtualization=true启用虚拟化。此功能需要用 .NET 5 以上。"英文说明一致:"Improve performance withEnableVirtualization=truewhen you have many options, .NET 5 or higher framework is required."。可见官方文档的核心口径就是:大量选项时设置该参数,且运行框架必须不低于 .NET 5。
四、大数据量 Select 的配套调优建议
开启EnableVirtualization只是第一步,要让千级选项的 Select 保持流畅,还需要结合 Select 的其他参数合理配置:
4.1 合理控制下拉容器高度
Select.razor.cs 定义了PopupContainerMaxHeight,默认值为256px,它是下拉选项滚动容器的高度上限:
[Parameter] public string PopupContainerMaxHeight { get; set; } = "256px";虚拟化组件的渲染窗口正是建立在这个滚动容器之上的,因此这个高度直接决定了一次性渲染的选项行数。高度越大,虚拟化窗口内驻留的 DOM 越多;建议保持默认或按实际布局适当调整,避免设置过大导致虚拟化收益下降。
4.2 充分使用搜索来缩小候选集
开启EnableSearch(Mode = Tags时始终为 true)后,下拉列表的渲染仍走 FilterOptionItems:每输入一个字符,就遍历SelectOptionItems并通过FilterExpression或默认的标签包含匹配逻辑(item.Label?.Contains(searchValue, StringComparison.InvariantCultureIgnoreCase))筛选出匹配项,不匹配项被标记为IsHidden。由于虚拟化分支只渲染未隐藏的项(Where(x => !x.IsHidden)),搜索可以大幅缩减实际渲染规模,与虚拟化形成"双重过滤",这是大数据量场景最有效的组合拳。
4.3 谨慎对待分组与排序
排序通过SortByGroup/SortByLabel生效,其实现位于 SelectBase.razor.cs。需要注意两点:
- 虚拟化要求传入的
Items是排序后的完整列表,排序本身需要对全量数据执行OrderBy,在数千条数据下开销仍在可控范围; - 如前文所述,启用
GroupName分组后走SelectOptionGroup渲染路径,不会经过虚拟化分支,因此分组模式不适合与大数据量虚拟化同时使用。
4.4 关注数据源变化时的重建成本
Select 会对DataSource做变更检测(EvaluateDataSourceChange,见 Select.razor.cs),并在数据源变化时通过CreateDeleteSelectOptions重建/增量更新SelectOptionItems。对大数据量而言,应避免频繁整体替换DataSource引用,尽量在初始化阶段一次性提供完整数据,以减少选项集合重建与虚拟化窗口重算的开销。
五、适用边界与注意事项
- 框架版本:虚拟化特性依赖 .NET 5+ 的
Virtualize组件。在 .NET 5 以下目标框架下编译时EnableVirtualization参数不会存在,项目需将目标框架提升至 .NET 5 或更高; - 适用于
DataSource模式:虚拟化分支针对SortedSelectOptionItems的自动渲染路径设计,适合通过DataSource+LabelName/ValueName/DisabledName绑定的选项;手工通过ChildContent/SelectOptions拼装大量SelectOption时,选项由调用方渲染,虚拟化的收益有限; - 选择规模阈值:几十到几百条选项时全量渲染本身开销很低,虚拟化并不能带来可感知的提升,反而引入额外的滚动窗口计算;建议在选项数达到千级时再启用;
- 与搜索协同:
EnableSearch+EnableVirtualization是官方 Demo 的标准组合,搜索过滤会先于虚拟化缩小候选集,两者叠加可获得最好的大数据量体验。
总结
EnableVirtualization是 ant-design-blazor Select 组件面向大数据量场景的关键性能开关:它在 .NET 5+ 运行时下,用 Blazor 内置Virtualize组件替换默认的ForeachLoop全量渲染(见 components/select/Select.razor),只渲染可视区内的选项。结合EnableSearch的搜索过滤、合理的PopupContainerMaxHeight以及一次性提供完整数据源的实践,即可让 2000 级乃至更多选项的 Select 保持流畅交互。实际开发中,可直接参照 BigData.razor 的单选/多选双示例落地实现。
- 前端
- UI组件
- 设计系统
【免费下载链接】ant-design-blazor
基于 Ant Design 与 Blazor 的前端组件库。让开发者解放生产力,实现更大价值。
相关推荐
10万行数据秒加载:ant-design-blazor虚拟滚动表格性能优化指南
10万行数据秒加载:ant design blazor虚拟滚动表格性能优化指南 一、为什么传统表格在大数据面前不堪一击? 当你的Blazor应用表格需要渲染10
UI组件前端Ant Design表单虚拟滚动:大数据表单性能优化方案
Ant Design表单虚拟滚动:大数据表单性能优化方案 在企业级应用开发中,表单承载着数据收集的核心功能。当面对成百上千条数据录入需求时,传统表单渲染方式往往
UI组件前端设计系统大数据列表性能优化终极指南:Ant Design Landing虚拟滚动实现方案
大数据列表性能优化终极指南:Ant Design Landing虚拟滚动实现方案 Ant Design Landing作为蚂蚁金服设计体系的重要组成部分,提供了
前端文档
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考