Maui Blazor 中文社区 QQ群:645660665

对于 Blazor 组件虚拟化支持flex-wrap: wrap与网格布局的研究 [一]

接上篇文章 Blazor 通过组件虚拟化提高性能

本文源码

https://github.com/densen2014/Blazor100/blob/master/b24Virtualization/BlazorVirtualization/Pages/VirtualizedFlex1.razor

问题

我想使用虚拟化来呈现项目。我使用的是弹性布局,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伪元素。

任何阻止间隔物和内容元素呈现为单个垂直堆栈的方法,或导致内容项高度变化的方法,都会阻止Virtualize组件正常运行。

深入

既然已经了解原理,那我们可不可以转换一下思路, 把元素分组渲染,每行渲染一批,这样高度不就是固定了吗?

使用 LINQ 将 Orders 按 itemsPerRow 分组

  1. 添加了 GroupedOrders 属性,用于存储按 itemsPerRow 分组的订单。
  2. 在 OnInitialized 方法中,使用 LINQ 将 Orders 按 itemsPerRow 分组,并将结果赋值给 GroupedOrders。
  3. 在 Virtualize 组件中,使用 GroupedOrders 作为数据源,并在内部循环中显示每个订单。

分组代码

<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 等参数, 配合查询父元素/屏幕宽度,就能自适应调节了.

对于 Blazor 组件虚拟化支持flex-wrap: wrap与网格布局的研究 [二]

本文源码

https://github.com/densen2014/Blazor100/blob/master/b24Virtualization/BlazorVirtualization/Pages/VirtualizedFlex1.razor

posted @ 2025-01-17 17:51  AlexChow  阅读(1597)  评论(0)    收藏  举报