对于.NET MAUI Blazor 应用支持的平台需要 Android 7.0 (API 24) 或更高版本,但是早期安卓版本设备硬件的性能有限,导致实际无法流畅运行应用。
本文章是使用MASA Blazor组件在MAUI框架开发低端安卓PDA应用的性能优化方案。.
以下是开发人员根据Blazor官方文档中的《ASP.NET Core Blazor 性能最佳做法》(https://learn.microsoft.com/zh-cn/aspnet/core/blazor/performance?view=aspnetcore-7.0)进行呈现效率优化的举例说明:
1. 减少组件的复杂性
1.1. 减少嵌套组件
将循环中的子组件直接写到父组件,避免呈现的子组件过多的重复开销
//原代码 <SelectOrderItem>子组件的代码需要直接写在当前页<MVirtualScroll Items="_alls" TItem="RunningOrderDto" Height="@("100%")" Width="@("100%")"><ItemContent><SelectOrderItem Item="context" OnCheckedChange="OnItemSelected" /> //此处待优化</ItemContent></MVirtualScroll>//新代码<MVirtualScroll Items="_alls" TItem="RunningOrderDto" Height="@("100%")" Width="@("100%")"><ItemContent><div Class="@($"mx-0 px-0 mt-2 {GetClass(Item.Disable,Item.Selected)}")" Style="border-top:solid 1px" @onclick="OnCheck"><div Class="mx-1 px-0 py-1 d-flex justify-space-between"><div class="pa-0 py-0"><div class="pa-0 py-0 font-3 text-left">物料名称:@Item.Name</div><div class="pa-0 py-0 mt-2 font-3 text-left">工单编号:@Item.ProdId</div></div><MIcon Class="@($"mt-4 {GetColor(Item.Selected)}")" Size="24">mdi-checkbox-marked-circle-outline</MIcon></div></div></ItemContent></MVirtualScroll>@code{[Parameter]public Action<RunningOrderDto> OnCheckedChange { get; set; }private async void OnCheck(){Item.Selected = !Item.Selected;if (OnCheckedChange != null)OnCheckedChange.Invoke(Item);}}
1.2. 减少非必要的组件使用
-
替换非必要的<MList>列表组件和<MCheckbox>复选框组件的联合使用
//原代码<MList Flat ThreeLine Class="pt-0"><MListItemGroup>@foreach (var line in _list){<MListItem><MListItemAction><MCheckbox @bind-Value="line.isSelect" Disabled="@(item.posted==NoYes.Yes)" OnClick="()=>checkboxChange()"></MCheckbox></MListItemAction><MListItemContent><MListItemTitle>@line.itemId<MChip Small Class="@getStatusColor(line)">@(line.isReceive ? "已收" : line.isSend ?"已发": "未发")</MChip></MListItemTitle><MListItemSubtitle>@line.itemName</MListItemSubtitle><MListItemSubtitle>@line.inventLocationId - @line.inventLocationIdTo</MListItemSubtitle></MListItemContent></MListItem><MDivider></MDivider>}</MListItemGroup></MList>//新代码@foreach (var line in _list) {<div Class="mx-0 px-0 mt-2"><div Class="@($"{getCheckColor(context)} mx-1 px-0 py-1 d-flex justify-space-between")"@onclick="()=>checkboxChange(context)"><div><MIcon Class="mt-6" Size="24">mdi-checkbox-marked-circle-outline</MIcon></div><div><div class="text-left text-h6">@context.itemId<MChip Small Class="@getStatusColor(context)">@(context.isReceive ? "已收" : context.isSend ? "已发" : "未发")</MChip></div><div class="text-left">@context.itemName</div><div class="text-left">@context.inventLocationId - @context.inventLocationIdTo</div></div></div></div><MDivider></MDivider>}
-
替换非必要的<MRow><MCol>行列组件,如果 Cols=12 那Row Col组件是没必要使用的
//原代码<MRow Class="ma-0 pa-0 mt-2"><MCol Cols="12" Class="pa-1"><PMobileDateTimePicker Precision="DateTimePrecision.Hour" Value="@_request.StartDate"ValueChanged="async (start)=>await OnChangeSearchAsync(start:start)"><ActivatorContent><MButton Color="primary" Class="text-capitalize" Text @attributes="@context.Attrs">@(_request.StartDate.Format("yyyy/M/d H:m"))</MButton></ActivatorContent></PMobileDateTimePicker></MCol><MCol Cols="12" Class="pa-1"><PMobileDateTimePicker Precision="DateTimePrecision.Hour" Value="@_request.EndDate"ValueChanged="async (end)=>await OnChangeSearchAsync(end:end)"><ActivatorContent><MButton Color="primary" Class="text-capitalize" Text @attributes="@context.Attrs">@(_request.EndDate.Format("yyyy/M/d H:m"))</MButton></ActivatorContent></PMobileDateTimePicker></MCol></MRow><MRow Class="pa-0 ma-0 py-2">@if (Items == null || !Items.Any()){<MCol Cols="12" Class="text-center pa-0 ma-0 header-footer-content">@("没有数据")</MCol>}</MRow>//新代码<div Class="ma-0 pa-0 mt-2 d-flex justify-space-between"><div Class="pa-1"><PMobileDateTimePicker Precision="DateTimePrecision.Hour" Value="@_request.StartDate"ValueChanged="async (start)=>await OnChangeSearchAsync(start:start)"><ActivatorContent><MButton Color="primary" Class="text-capitalize" Text @attributes="@context.Attrs">@(_request.StartDate.Format("yyyy/M/d H:m"))</MButton></ActivatorContent></PMobileDateTimePicker></div><div Class="pa-1"><PMobileDateTimePicker Precision="DateTimePrecision.Hour" Value="@_request.EndDate"ValueChanged="async (end)=>await OnChangeSearchAsync(end:end)"><ActivatorContent><MButton Color="primary" Class="text-capitalize" Text @attributes="@context.Attrs">@(_request.EndDate.Format("yyyy/M/d H:m"))</MButton></ActivatorContent></PMobileDateTimePicker></div></div>@if (Items == null || !Items.Any()){<div class="text-center pa-0 ma-0 header-footer-content">@("没有数据")</div>}
2. 关闭过渡动画
在低端设备中,关闭过渡动画可以有效减少组件卡顿的视觉效果。例如 Menu 组件,通过参数置空 Transition="" 设置。
<MMenu Transition=""><ActivatorContent><MButton Color="secondary" Class="ma-2" @attributes="context.Attrs">Scroll Y Transition</MButton></ActivatorContent><ChildContent><MList><MListItem><MListItemTitle>@("Item")</MListItemTitle></MListItem></MList></ChildContent></MMenu>
3. 使用虚拟滚动
对于需要展示大量数据的组件,可以使用 MVirtualScroll 组件,只渲染可见区域的数据,减少渲染时间和内存占用。
<MVirtualScroll Items="_items" TItem="Item" Height="@("100%")" Width="@("100%")"><ItemContent><div>代码:列表内容</div></ItemContent></MVirtualScroll>
4. 使用PPageContainer组件
对于一些非业务数据相关的活动页面缓存到DOM中,每次进入页面立即载入缓存,可以避免重复计算和渲染,快速响应。使用 PageTabs 页面选项卡组件的 一个容器组件 PPageContainer,其中 ExcludedPattners 属性接受一个匹配路径的正则表达式列表,匹配成功的路径对应的页面的内容将不会在离开页面后被缓存在DOM中。
<MMain><MErrorHandler ShowDetail><ChildContent><PPageContainer ExcludedPatterns="s_excludedPatterns">@Body</PPageContainer></ChildContent></MErrorHandler></MMain>@code {/// <summary>/// 不使用DOM缓存/// </summary>private static readonly string[] s_excludedPatterns ={"/Login"}}
5.使用性能优化工具
可以使用性能分析工具来检测和优化组件的性能问题,找出性能瓶颈并进行优化。