Blazor Server(.NET6)加载模态框仅在方法完成后显示问题
Blazor Server加载模态框延迟显示问题解决
问题背景
在.NET 6 Blazor Server项目中,需要实现点击按钮后立即弹出加载模态框,显示“loading...”状态,等待5秒后切换为“Ready”并更新计数器数值。但实际运行时,模态框要等5秒延迟结束后才会打开,直接显示“Ready”状态,完全达不到预期效果。
复现代码
Counter.razor
@page "/counter" <PageTitle>Counter</PageTitle> <h1>Counter</h1> <p role="status">Current count: @currentCount</p> <button class="btn btn-primary" @onclick="IncrementCount">Click me</button> <Modal @ref="modal"> <Title>This is a <em>Title!</em></Title> <Body> @if (loading) { <p> Incrementing please wait... </p> } else { <p> Ready </p> } </Body> <Footer> <button type="button" class="btn btn-secondary" data-dismiss="modal" @onclick="() => modal.Close()">Close</button> </Footer> </Modal> @code { private Modal modal; private int currentCount = 0; private bool loading; private async void IncrementCount() { loading = true; modal.Open(); StateHasChanged(); Task.Delay(5000).Wait(); currentCount++; loading = false; } }
Modal.razor
<div class="modal @modalClass" tabindex="-1" role="dialog" style="display:@modalDisplay; overflow-y: auto;"> <div class="modal-dialog modal-lg" role="document"> <div class="modal-content"> <div class="modal-header"> <h5 class="modal-title">@Title</h5> <button type="button" class="close" data-dismiss="modal" aria-label="Close" @onclick="Close"> <span aria-hidden="true">×</span> </button> </div> <div class="modal-body"> @Body </div> <div class="modal-footer"> @Footer </div> </div> </div> </div> @if (showBackdrop) { <div class="modal-backdrop fade show"></div> } @code { [Parameter] public RenderFragment Title { get; set; } [Parameter] public RenderFragment Body { get; set; } [Parameter] public RenderFragment Footer { get; set; } private string modalDisplay = "none;"; private string modalClass = ""; private bool showBackdrop = false; public async void Open() { modalDisplay = "block;"; modalClass = "show"; showBackdrop = true; } public void Close() { modalDisplay = "none"; modalClass = ""; showBackdrop = false; } }
问题原因
- 同步阻塞UI线程:使用
Task.Delay(5000).Wait()进行同步等待,直接阻塞了Blazor的UI主线程,导致所有UI更新操作(包括模态框显示)都被暂停,直到阻塞结束后才会一次性执行。 - 状态更新未正确触发:Modal组件的
Open方法修改了状态变量,但没有调用StateHasChanged()通知框架重新渲染组件;Counter组件中调用StateHasChanged()后,由于UI线程被阻塞,无法立即执行渲染。 - 不当使用
async void:虽然Blazor事件处理方法允许使用async void,但配合正确的异步操作,使用async Task会更规范。
正确解决方案
1. 修改Counter组件的IncrementCount方法
将同步等待改为异步等待,确保UI线程不被阻塞,并正确触发状态更新:
private async Task IncrementCount() { loading = true; modal.Open(); // 触发UI更新,立即显示模态框和loading状态 await InvokeAsync(StateHasChanged); // 异步等待,释放UI线程处理渲染 await Task.Delay(5000); currentCount++; loading = false; // 更新UI,显示Ready状态和新计数 await InvokeAsync(StateHasChanged); }
2. 修正Modal组件的Open方法
移除不必要的async,并添加状态更新触发:
public void Open() { modalDisplay = "block;"; modalClass = "show"; showBackdrop = true; // 通知组件状态变化,触发渲染 StateHasChanged(); }
临时替代方案
如果需要快速验证,也可以通过后台线程触发UI更新(但不推荐作为长期方案):
private async void IncrementCount() { await Task.Run(() => { loading = true; modal.Open(); InvokeAsync(StateHasChanged); }); Task.Delay(2000).Wait(); currentCount++; loading = false; StateHasChanged(); }
说明:该方案通过
Task.Run将模态框显示操作放到后台线程,再通过InvokeAsync触发UI更新,避免阻塞主线程。但仍然使用了同步等待Wait(),不符合异步编程规范,仅作临时验证使用。
内容的提问来源于stack exchange,提问作者mariano desivo
相关产品推荐
相关产品推荐

