Blazor 服务器应用程序中的频繁页面更新
Frequent page updates in blazor server application
在 Blazor 服务器应用程序中,是否可以经常发送事件和调用 StateHasChanged
,例如每秒 500 次?
我的一个页面需要对外部事件做出反应,然后相应地更新其状态。我找到了以下解决方案:
- 创建检测外部事件并调用 C# 事件的服务。
- 将服务注入剃须刀页面。
- 在页面中,连接到事件并在处理程序中调用
InvokeAsync(() => StateHasChanged())
。
这已经可以正常工作了。但是,事件可能会发生的非常频繁,例如每秒500次,我担心客户端和服务器的性能。不幸的是,我不明白哪一部分发生在服务器上,哪一部分发生在客户端上,以及它们之间发送了哪些数据。
- 事件实际上每秒从服务器发送到客户端 500 次吗?我认为这会消耗大量带宽。
- 客户端是否在每次调用
StateHasChanged
后实际呈现页面?我认为这会给客户端带来很高的 CPU 负载。
回答您的一些问题,因为您的 运行 在服务器模式下,所有实际工作都在 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++;
}
在 Blazor 服务器应用程序中,是否可以经常发送事件和调用 StateHasChanged
,例如每秒 500 次?
我的一个页面需要对外部事件做出反应,然后相应地更新其状态。我找到了以下解决方案:
- 创建检测外部事件并调用 C# 事件的服务。
- 将服务注入剃须刀页面。
- 在页面中,连接到事件并在处理程序中调用
InvokeAsync(() => StateHasChanged())
。
这已经可以正常工作了。但是,事件可能会发生的非常频繁,例如每秒500次,我担心客户端和服务器的性能。不幸的是,我不明白哪一部分发生在服务器上,哪一部分发生在客户端上,以及它们之间发送了哪些数据。
- 事件实际上每秒从服务器发送到客户端 500 次吗?我认为这会消耗大量带宽。
- 客户端是否在每次调用
StateHasChanged
后实际呈现页面?我认为这会给客户端带来很高的 CPU 负载。
回答您的一些问题,因为您的 运行 在服务器模式下,所有实际工作都在 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++;
}