• Blazor/Hybird 触屏下单程序调优笔记


    环境 Blazor Net8.0 + FreeSql + Bootstrap Blazor 组件

    以下都是自己瞎琢磨的和官网资料搬运,肯定有不少错漏和不合理的地方,非常希望各位大佬评论区给我建议和意见.

    1. 组件化需要提升渲染性能的组件,例如触摸屏显示每个商品下单数量的商品列表

    避免不必要地呈现组件子树, 执行一些初始化渲染后设置按需渲染, 外部控制按需渲染参数

    
    //按需渲染
    [Parameter]
    public bool RenderQuantity { get; set; } = true;
    
    protected override bool ShouldRender() =>  RenderQuantity; 
    
    protected override void OnAfterRender(bool firstRender)
    {
        if (firstRender)
        {
          //执行一些初始化渲染后设置按需渲染
    
          你的初始化渲染();
    
          StateHasChanged();
          RenderQuantity = false;
        }
    }
    
    protected override async Task OnParametersSetAsync()
    {
        if (RenderQuantity)
        {
            _ = Task.Run(async () =>
            {
                await Task.Delay(500);
                RenderQuantity = false;
            });
        }
    }
    

    调用的页面,添加 RenderQuantity 控制渲染数量变化时机. 避免 StateHasChanged() 执行后渲染子组件.

    
    
    private bool RenderQuantity { get; set; }
    

    2. API和UI分离: 例如更新订单数量,操作内存数据渲染到UI,不要等待后台查询订单详单列表后在刷新UI. 异步执行后台服务更新订单数量.

    UI 更新数量

    Task UpdateQuantity(string userCode, int thisQuantity= 1)
    {
         //更新订单数量,操作内存数据,不查询数据库,提高性能. API和UI分离
         var itemOrdersMenu = OrdersMenu.Where(a => a.UserCode == userCode).FirstOrDefault();
         if (itemOrdersMenu != null)
         {
              itemOrdersMenu.Quantity = thisQuantity;
              RenderQuantity = true;
         } 
    
         _ = Task.Run(async () =>
         {
              //更新订单数量,返回合计
              var newOrderdetailsDto = DataService.UpdateQuantity(userCode,thisQuantity);
              if (newOrderdetailsDto.ForceQuantity!=null)
              {
                   //处理脏数据问题,更新订单数量为强制数量
                   item.Quantity = newOrderdetailsDto.ForceQuantity.Value;
    
                   if (itemOrdersMenu != null)
                   {
                     itemOrdersMenu.Quantity = newOrderdetailsDto.ForceQuantity;
                     RenderQuantity = true; 
                   }
                   await InvokeAsync(StateHasChanged);
                }
         });
         return Task.CompletedTask;
    }
    

    Tips: 对于长时间不操作的订单界面,例如收银台桌面程序(Blazor/Blazor Hybird), 可以设置一个 UI 更新数量定时器, 例如间隔5分钟重新刷新整页.

    3. 脏数据: 因为是多终结点程序(PC浏览器/手机浏览器/PDA/桌面版),不可避免存在脏数据问题. 需要变更单行订单数量刷新UI后, 获取后台单行订单数量,比对,有异常则重新执行 RenderQuantity 或 StateHasChanged 更新单行订单数量.

    4. 服务端不要直接更新订单数量,改为原子操作, 采用 a.Quantity = a.Quantity + thisQuantity 方式

    服务端 DataService.UpdateQuantity 方法:

    fsql.Update()
         .Set(a => new ResOrderDetails()
         {
              Quantity = a.Quantity + thisQuantity
         })
         .Where(a => a.OrderID == orderID && a.UserCode == userCode)
         .ExecuteAffrows();
    
    

    5. Button 尽可能使用 OnClickWithoutRender 方法: 点击按钮时触发此事件并且不刷新当前组件,用于提高性能时使用.

    6. 使用 CascadingValue 组件具有可选的 IsFixed 参数

    • 如果 IsFixed 为 false(默认值),则级联值的每个接收方都会将订阅设置为接收更改通知。 由于订阅跟踪,每个 [CascadingParameter] 的开销大体上都要比常规 [Parameter] 昂贵。

    • 如果 IsFixed 为 true(例如,),则接收方会接收初始值,但不会将订阅设置为接收更新。 每个 [CascadingParameter] 都是轻型的,并不比常规 [Parameter] 昂贵。

    如果有大量其他组件接收级联值,则将 IsFixed 设置为 true 可提高性能。 只要有可能,就应将级联值的 IsFixed 设置为 true。 当提供的值不会随时间而改变时,可以将 IsFixed 设置为 true。

    在组件将 this 作为级联值传递时,也可以将 IsFixed 设置为 true:

    
        
    
    

    7. 不要过快触发事件

    某些浏览器事件极频繁地触发。 例如,onmousemove 和 onscroll 每秒可以触发数十或数百次。 在大多数情况下,不需要经常执行 UI 更新。 如果事件触发速度过快,可能会损害 UI 响应能力或消耗过多的 CPU 时间。

    请考虑使用 JS 互操作来注册不太频繁触发的回调,而不是使用快速触发的本机事件。 例如,以下组件显示鼠标的位置,但每 500 毫秒最多只能更新一次:

    @implements IDisposable
    @inject IJSRuntime JS
    
    

    @message

    Move mouse here
    @code { private ElementReference mouseMoveElement; private DotNetObjectReference? selfReference; private string message = "Move the mouse in the box"; [JSInvokable] public void HandleMouseMove(int x, int y) { message = $"Mouse move at {x}, {y}"; StateHasChanged(); } protected override async Task OnAfterRenderAsync(bool firstRender) { if (firstRender) { selfReference = DotNetObjectReference.Create(this); var minInterval = 500; await JS.InvokeVoidAsync("onThrottledMouseMove", mouseMoveElement, selfReference, minInterval); } } public void Dispose() => selfReference?.Dispose(); }

    相应的 JavaScript 代码会注册用于鼠标移动的 DOM 事件侦听器。 在此示例中,事件侦听器使用 Lodash 的 throttle 函数来限制调用速率:

    
    
    

    8. 使用缓存

    private ConcurrentDictionary LazyTabCache { get; } = new();
    
    private RenderFragment RenderTabItemContent(TabItem item) => builder =>
    {
            if (item.IsActive)
            {
                var content = _errorContent ?? item.ChildContent;
                builder.AddContent(0, content);
                _errorContent = null;
                if (IsLazyLoadTabItem)
                {
                    LazyTabCache.AddOrUpdate(item, _ => true, (_, _) => true);
                }
            }
            else if (!IsLazyLoadTabItem || item.AlwaysLoad || LazyTabCache.TryGetValue(item, out var init) && init)
            {
                builder.AddContent(0, item.ChildContent);
            }
    };
    private RenderFragment? _errorContent;
    
    private static readonly ConcurrentDictionary _cachedInstances = new();
    
    if (!_cachedInstances.TryGetValue(targetType, out result))
    {
        result = Create(targetType);
        if (result is null)
        {
          return false;
        }
    
        _cachedInstances.TryAdd(targetType, result);
    }
    
    

    学习资料

    Blazor 性能最佳做法

    按需渲染,手动管理 UI 刷新

  • 相关阅读:
    技术干货 | AECRNet:基于对比学习的紧凑图像去雾方法
    错的不是世界,是我
    招聘程序员(软件开发工程师),如何做岗位胜任力测评?
    MybatisPlus【SpringBoot】 3 基本CRUD
    十一、动态规划题目相关
    【QT进阶】Qt线程与并发之QtConcurrent的简单介绍
    tiup mirror
    Tonka Finance,BTCFi 浪潮的发动机
    心理学杂文
    大白话讲讲 Go 语言的 sync.Map(二)
  • 原文地址:https://www.cnblogs.com/densen2014/p/18174198