了解组件虚拟化如何优化 Blazor 应用程序的渲染性能.
介绍
在使用数据驱动的应用程序时,我们迟早要在列表中呈现数量多得令人难以忍受的项目。作为开发人员,我们总是希望用户界面尽可能清晰,并且只包含执行特定操作所需的数据。
但是,如果产品所有者要求呈现 10000 或 100000 个项目怎么办?如果理由是“客户需要它”怎么办?作为一名优秀的开发人员,我们会想出一个可行的解决方案。
渲染数千或数十万个项目的根本问题是渲染需要时间、占用大量内存,并且通常会让网站感觉很慢。
组件虚拟化是 Blazor 的一项功能,可以帮助我们找到此类场景的解决方案。
没有虚拟化的问题
在实现虚拟化之前,我们首先需要创建一个示例,展示在 Blazor 中渲染数千个项目是什么样子。
考虑以下在屏幕上呈现订单的代码。
<h1>Orders</h1>
@foreach (var order in Orders)
{
<div style="display:flex;">
<div style="width: 400px;">@order.Id</div>
<div>$ @order.Value</div>
</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_000; i++)
{
Orders.Add(new Order(Guid.NewGuid(), random.Next(20, 9999)));
}
}
}
Order我们定义一个包含Id和属性的对象列表Value。在OnInitialized生命周期方法中,我们创建十万个订单并将它们添加到Orders列表中。
在组件模板中,我们有一个foreach语句来渲染屏幕上的所有项目。在此示例中,我们只div为每个订单渲染三个项目。在更复杂的设置中,这可能是一棵完整的对象树,其中包括更多 HTML 元素。
尽管我们使用了相对简单的 HTML 标记,但在导航到页面时我们已经明显感觉到延迟。此外,由于使用了1.4 GB 的内存,这绝对不是理想的情况。
如何实现虚拟化
Blazor 提供了一个内置Virtualize组件,使虚拟化组件变得简单。最好的部分是:我们可以虚拟化任何东西。无论是另一个 Blazor 组件、简单的 HTML 元素还是两者的混合。
下面的模板代码展示了如何虚拟化上面介绍的示例。
<Virtualize Items="Orders" Context="order">
<div style="display:flex;">
<div style="width: 400px;">@order.Id</div>
<div>$ @order.Value</div>
</div>
</Virtualize>
该Virtualize组件公开了一些属性。最重要的是Items和Context。我们将要虚拟化的项目列表作为Items属性的参数提供。我们为每个项目提供一个名称作为属性的值Context。
然后我们可以使用组件内的顺序变量Virtualize来定义每个项目的模板。
使用组件虚拟化运行 Blazor 应用程序时,我没有注意到渲染页面时出现延迟。此外, Google Chrome 中的内存消耗已从未使用虚拟化之前的 1.4 GB降至100 MB 。
通过将项目代码包装在 Virtualize 组件中这样简单的事情,我们可以实现快速的页面加载和更少的内存消耗。
它是如何工作的?
您可能想知道它在幕后是如何工作的。很容易假设该Virtualize组件实现分页,这意味着它只加载屏幕上可查看的项目。不幸的是,事情没那么简单。
当我们在方法中初始化项目列表时OnInitialized,我们已经将所有项目提取到内存中。将模板代码包装在Virtualize组件中不会改变这一点。项目仍在内存中。
另一方面,如果没有虚拟化,页面将为div列表中的每个项目呈现一个。
控制渲染行为
滚动时,该Virtualize组件会呈现附加组件,使用户感觉无缝。
该Virtualize组件有一个内部实现,用于决定在屏幕上当前可见的内容之外渲染多少元素。内部算法基于项目的高度及其容器的高度。
我们可以使用Virtualize组件的OverscanCount属性来改变渲染的附加项目数量。
但是,屏幕上只呈现了有限数量的元素。我们可以通过打开开发人员工具并查看元素选项卡来观察这一点。使用虚拟化,我们只能看到有限数量的div元素。
<Virtualize Items="Orders" Context="order" OverscanCount="15">
<div style="display:flex;">
<div style="width: 400px;">@order.Id</div>
<div>$ @order.Value</div>
</div>
</Virtualize>
我通常会尽可能保留默认值。有时,根据单个项目内容的复杂程度和大小,您需要手动设置OverscanCount属性以更好地控制其渲染行为。
对于我上面的例子,默认实现会渲染大约 15 个附加项目。
延迟加载项目
但是如果加载项目的成本很高怎么办?如果我们只想加载屏幕上实际可见的项目怎么办?是的,这是可能的。
延迟加载使用该ItemsProvider属性而不是组件Items的属性Virtualize。
<Virtualize ItemsProvider="@LoadOrders" Context="order">
<div style="display:flex;">
<div style="width: 400px;">@order.Id</div>
<div>$ @order.Value</div>
</div>
</Virtualize>
重要提示:我们既可以使用Items包含所有项目的集合的属性,也可以ItemsProvider使用加载项目的方法的属性,但不能同时设置它们。否则,我们将InvalidOperationException在运行时得到一个。
现在我们看一下LoadOrders放置在@codeLazyLoading页面组件部分中的方法。
private ValueTask<ItemsProviderResult<Order>> LoadOrders(ItemsProviderRequest request)
{
StartIndex = request.StartIndex;
Count = request.Count;
StateHasChanged();
var filteredOrders = Orders.Skip(request.StartIndex)
.Take(request.Count);
var result = new ItemsProviderResult<Order>(filteredOrders, Orders.Count());
return ValueTask.FromResult(result);
}
虚拟化和延迟加载的陷阱
提供的Virtualize组件做了很多工作来减少显示大量数据列表所需的渲染性能。
但是,也有一些限制。例如,所有项目的高度必须相同。否则,Virtualize组件无法计算滚动范围,因此不知道要渲染什么。
此外,如果每 40-50 个项目调用一次昂贵的 API,则会增加很多开销。例如,您可能会有 HTTP 请求,包括每个请求的延迟。有时,最好预先加载所有数据,即使用户需要等到整个数据集下载完毕。
与软件工程中常见的情况一样,此功能并非每次都可用。它更像是工具箱中的附加项。在有意义时使用它,在不合适时使用其他功能。
结论
内置组件允许我们通过仅渲染屏幕上可见的项目Virtualize来高效地渲染大型数据集。该组件使用内部算法来决定一次显示多少个项目。
我们可以使用该Items属性提供预填充的数据列表或ItemProvider延迟加载数据。正如本文所述,这两种方法各有优缺点。
这OverscanCount允许我们控制同时渲染的项目数量。设置此属性有助于根据您的用例调整行为。当使用延迟加载的 API 调用需要很长时间时,请增加此值。渲染简单的内存组件时,请减少此值以提高页面加载性能。
总而言之,内置Virtualize组件可以帮助您编写更高效的 Blazor 应用程序。
Flex布局使用虚拟化
参考链接
https://www.telerik.com/blogs/blazor-basics-improved-performance-component-virtualization
https://learn.microsoft.com/en-us/aspnet/core/blazor/components/virtualization?view=aspnetcore-9.0
标签:虚拟化,项目,Virtualize,渲染,组件,Blazor,加载 From: https://www.cnblogs.com/densen2014/p/18677409