接上篇文章 Blazor 通过组件虚拟化提高性能
问题
我想使用虚拟化来呈现项目。我使用的是弹性布局,flex-wrap: wrap当宽度完全用完时,我会将这些项目包裹起来( )。第一个项目将按预期呈现(只要您没有触摸滚动条)。开始滚动时,所有项目都会闪烁,并且控件不再可用。
<PageTitle>Virtualized Orders</PageTitle><h1>Virtualized Orders</h1><div style="height: 600px; overflow-y: scroll; display: flex; width: 600px; flex-direction: row; flex-wrap: wrap; "><Virtualize Items="Orders" Context="order" OverscanCount="15"><div style="width: 100px;height:100px; background-color:cadetblue;padding:10px; margin: 10px;"><div>$ @order.Value</div></div></Virtualize>
</div>@code {public record Order(Guid Id, int Value);public IList<Order> Orders { get; set; } = new List<Order>();protected override void OnInitialized(){var random = new Random();for (int i = 0; i < 100; i++){Orders.Add(new Order(Guid.NewGuid(), random.Next(20, 9999)));}}
}
原因是这种方案不受支持。要了解支持哪些内容,您可以阅读 Blazor 虚拟化文档。
Virtualize
Virtualize在下列条件下工作:
-
所有呈现的内容项(包括占位符内容)的高度都相同。这样就可以计算出哪些内容对应于给定的滚动位置,而无需先获取每个数据项并将数据呈现到 DOM 元素中。
-
间隔行和内容行均呈现在单个垂直堆栈中,每个项目都填充整个水平宽度。在典型用例中,Virtualize与元素配合使用div。如果您使用 CSS 创建更高级的布局,请记住以下要求:
- 滚动容器样式需要display具有以下任意值:
- block( a 的默认值div)。
- table-row-group( a 的默认值tbody)。
- flexflex-direction设置为。column确保Virtualize
组件的直接子组件不会在 flex 规则下收缩。例如,添加.mycontainer > div { flex-shrink: 0 }。
- 内容行样式需要display具有以下任一值:
- block( a 的默认值div)。
- table-row( a 的默认值tr)。
- 不要使用 CSS 来干扰间隔元素的布局。间隔元素display的值为block,除非父元素是表格行组,在这种情况下它们的默认值为table-row。不要试图影响间隔元素的宽度或高度,包括让它们具有边框或content伪元素。
- 滚动容器样式需要display具有以下任意值:
任何阻止间隔物和内容元素呈现为单个垂直堆栈的方法,或导致内容项高度变化的方法,都会阻止Virtualize
深入
既然已经了解原理,那我们可不可以转换一下思路, 把元素分组渲染,每行渲染一批,这样高度不就是固定了吗?
分组代码
<PageTitle>Virtualized Orders</PageTitle><h1>Virtualized Orders</h1><div style="height: 370px; overflow-y: scroll; width: 380px; "><Virtualize Items="GroupedOrders" Context="orderGroup" ItemSize="16.667f"><div style="display: flex; flex-direction: row; flex-wrap: wrap; ">@foreach (var order in orderGroup){<div style="width: 100px; height: 100px; background-color: cadetblue; padding: 10px; margin: 10px;"><div>$ @order.Value</div></div>}</div></Virtualize>
</div>@code {int itemsPerRow = 3;public record Order(Guid Id, int Value);public IList<Order> Orders { get; set; } = new List<Order>();public IList<IEnumerable<Order>>? GroupedOrders { get; set; }protected override void OnInitialized(){var random = new Random();for (int i = 0; i < 100; i++){Orders.Add(new Order(Guid.NewGuid(), random.Next(20, 9999)));}GroupedOrders = Orders.Select((order, index) => new { order, index }).GroupBy(x => x.index / itemsPerRow).Select(g => g.Select(x => x.order)).ToList();}
}
后续
可以试封装成组件, 公开 itemsPerRow 和 itemsHeight 等参数, 配合查询父元素/屏幕宽度,就能自适应调节了.