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

时间:2021-03-02 11:12:57

标签: blazor blazor-server-side

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

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

  • 创建检测外部事件并调用 C# 事件的服务。
  • 将服务注入 razor 页面。
  • 在页面中,连接到事件并在处理程序中调用 InvokeAsync(() => StateHasChanged())

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

  • 事件实际上每秒从服务器发送到客户端 500 次吗?我认为这会消耗大量带宽。
  • 客户端是否在每次调用 StateHasChanged 后实际呈现页面?我认为这会给客户端带来很高的 CPU 负载。

2 个答案:

答案 0 :(得分:3)

为了回答您的一些问题,当您在服务器模式下运行时,所有实际工作都在 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 将这些更改发送到客户端浏览器会话。

所以没有变化,没有客户端活动,只有大量的服务器绑定活动处理事件并计算出任何变化。对服务器的影响将取决于您有多少可用的服务器功率。

答案 1 :(得分:1)

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

enter image description here

@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++;
}