Blazor 服务器应用程序中的频繁页面更新

Frequent page updates in blazor server application

在 Blazor 服务器应用程序中,是否可以经常发送事件和调用 StateHasChanged,例如每秒 500 次?

我的一个页面需要对外部事件做出反应,然后相应地更新其状态。我找到了以下解决方案:

这已经可以正常工作了。但是,事件可能会发生的非常频繁,例如每秒500次,我担心客户端和服务器的性能。不幸的是,我不明白哪一部分发生在服务器上,哪一部分发生在客户端上,以及它们之间发送了哪些数据。

回答您的一些问题,因为您的 运行 在服务器模式下,所有实际工作都在 Blazor Hub 会话中进行。

调用 StateHasChanged 的真正作用是将 RenderFragment 排队到 Hub 会话中的渲染器队列中。这是来自 ComponentBase.

的部分代码
           _renderFragment = builder =>
            {
                _hasPendingQueuedRender = false;
                _hasNeverRendered = false;
                BuildRenderTree(builder);
            };

StateHasChanged 看起来像这样:

       protected void StateHasChanged()
        {
            if (_hasPendingQueuedRender) return;
            if (_hasNeverRendered || ShouldRender())
            {
                _hasPendingQueuedRender = true;
                try
                {
                    _renderHandle.Render(_renderFragment);
                }
                catch
                {
                    _hasPendingQueuedRender = false;
                    throw;
                }
            }
        }

StateHasChanged 仅在新渲染事件尚未排队时才将其排队。渲染后,渲染器差异引擎会检测到任何更改,并通过 SignalR 将这些更改发送到客户端浏览器会话。

所以没有更改,没有客户端 activity,只有大量服务器绑定 activity 处理事件并制定任何更改。对服务器的影响将取决于您可用的服务器能力。

看起来 Blazor 服务器每秒可以发送数百个更改:

@page "/"
Tics per second: <input type="range" min="1" max="2000" @bind="@CurrentValue" class="slider" id="myRange"> @CurrentValue
<div style="width:500px; height:10px; background-color: blue; position: relative;">
    <div class="ball" style="@position_txt"></div>
</div> <br/><br/>
<span>@DateTime.Now.ToString("HH:mm:ss")</span>
<span>Number of renders: @nRenders.ToString("N0")</span>
<button type="button" @onclick="start">start</button>
<style>
 .ball {width: 30px; height: 30px; top: -10px;
        position: absolute; background-color: blue;}
</style>
@code
{
    Int64 nRenders = 0, v = 1, position = 10, CurrentValue = 10;
    string position_txt => $"left: {position}px;";
    private static System.Timers.Timer aTimer = new System.Timers.Timer();
    protected void start()
    {
        move();
        aTimer.Elapsed += (source, e) => move();
        aTimer.AutoReset = true;
        aTimer.Enabled = !aTimer.Enabled;
    }
    protected void move()
    {
        aTimer.Interval = 1000.0/CurrentValue;
        position = (position+v);
        if (position>500 || position<0) v *= -1;
        InvokeAsync(StateHasChanged);
    }
    protected override void OnAfterRender(bool firstRender) => nRenders++;
}