You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

.NET 8 Blazor Server初始化异步加载界面卡顿问题求助

问题描述

我在新项目中使用.NET 8 Blazor Server/Client,基于Sync Fusion模板开发并集成了ASP.NET Identity功能。制作了一个展示服务器日志的Blazor Server页面,登录后进入该页面时,期望数据加载期间显示“Loading Seri Logs......”,但实际是数据库请求完成后才显示加载提示,还会重复触发数据库调用,最终才展示带数据的Grid。即使替换数据库调用为Task.Delay(1000),问题依旧。原本以为Blazor会在数据加载完成前显示加载状态,完成后再展示Grid,请问哪里操作有误?

模板项目中其他无异步调用的页面加载正常。


Seri-Log.Razor页面

@page "/maintenance/seri-log-server"
@using Microsoft.AspNetCore.Authorization
@using App.Client.Services
@using System.Net.Http.Headers
@using App.Shared.DTOs.SeriLog
@using App.Shared.Interfaces
@using Syncfusion.Blazor.Buttons
@using System.Text.Json
@using Syncfusion.Blazor.Grids
@using System.Net.Http.Json

@inject ILogger<SeriLogsServer> _logger;
@inject IMaintenanceStore maintenanceStore;

@attribute [Authorize]

<PageTitle>Seri Log Maintenance Server</PageTitle>


<h3>Start Local: @CurrenTimeLocalStart</h3>
<hr />

@if (Error != null)
{
    <div class="c-error">
        <h3>Error</h3>
        <h5>@Error</h5>
    </div>
}

@if (SeriLogs == null)
{
    <span>
        Loading Seri Logs......
    </span>
}
else
{
    <hr />
    <h3>End Local: @CurrenTimeLocalEnd</h3>
    <SfGrid ID="Grid" DataSource="@SeriLogs" @ref="Grid" AllowPaging="true" AllowFiltering="true" AllowReordering="true" AllowResizing="true" AllowGrouping="true" AllowExcelExport="true" AllowSelection="false">
        <GridFilterSettings Type="Syncfusion.Blazor.Grids.FilterType.FilterBar"></GridFilterSettings>
        <GridPageSettings PageSizes="true"></GridPageSettings>
        <GridSelectionSettings Type="Syncfusion.Blazor.Grids.SelectionType.Multiple"></GridSelectionSettings>
        <GridColumns>
            <GridColumn Field=@nameof(SeriLogDTO.Id) HeaderText="ID" IsPrimaryKey="true" TextAlign="TextAlign.Center" Width="120"></GridColumn>
            <GridColumn Field=@nameof(SeriLogDTO.TimeStamp) HeaderText="TimeStamp" IsPrimaryKey="false" TextAlign="TextAlign.Center" Width="120"></GridColumn>
            <GridColumn Field=@nameof(SeriLogDTO.Message) HeaderText="Message" IsPrimaryKey="false" TextAlign="TextAlign.Center" Width="500"></GridColumn>
            <GridColumn Field=@nameof(SeriLogDTO.Exception) HeaderText="Exception" IsPrimaryKey="false" TextAlign="TextAlign.Center" Width="200"></GridColumn>
            <GridColumn Field=@nameof(SeriLogDTO.MessageTemplate) HeaderText="MessageTemplate" IsPrimaryKey="false" TextAlign="TextAlign.Center" Width="300"></GridColumn>
            <GridColumn Field=@nameof(SeriLogDTO.Level) HeaderText="Level" IsPrimaryKey="false" TextAlign="TextAlign.Center" Width="200"></GridColumn>

            <GridColumn Field=@nameof(SeriLogDTO.Properties) HeaderText="Properties" IsPrimaryKey="false" TextAlign="TextAlign.Center" Width="200"></GridColumn>
            <GridColumn Field=@nameof(SeriLogDTO.LogEvent) HeaderText="LogEvent" IsPrimaryKey="false" TextAlign="TextAlign.Center" Width="200"></GridColumn>
        </GridColumns>
    </SfGrid>
}

<style>
    .c-error {
        padding: 5px;
        background: #fff0f4;
        color: #c51244;
    }
</style>

@code {

    SfGrid<SeriLogDTO>? Grid;

    private DateTime CurrenTimeLocalStart { get; set; }

    private DateTime CurrenTimeLocalEnd { get; set; }

    public IList<SeriLogDTO>? SeriLogs = null;

    public string Error = null;

    private bool isInitialized = false;


    protected override async Task OnInitializedAsync()
    {
        CurrenTimeLocalStart = DateTime.UtcNow.ToLocalTime();
        // SeriLogs = await maintenanceStore.ListRelated();
        await Task.Delay(2000);
        CurrenTimeLocalEnd = DateTime.UtcNow.ToLocalTime();
    }
}

App.Razor

<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="utf-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <base href="/" />
    <link rel="stylesheet" href="bootstrap/bootstrap.min.css" />
    <link rel="stylesheet" href="app.css" />
    <link rel="stylesheet" href="App.styles.css" />
    <link rel="icon" type="image/png" href="favicon.png" />
    <HeadOutlet />
    <link rel="stylesheet" href="_content/Syncfusion.Blazor.Themes/bootstrap5.css" />
@*     <link rel="stylesheet" href="_content/Syncfusion.Blazor.Themes/fluent.css" /> *@
    <script src="_content/Syncfusion.Blazor.Core/scripts/syncfusion-blazor.min.js" type="text/javascript"></script>
    <script src="js/localStorage.js"></script>
    <script src="js/logoutOnly.js"></script>
</head>
<body>
    <Routes />
    <script src="_framework/blazor.web.js" autostart="true"></script>
 @*    <script src="js/boot.js"></script> *@

    <script>
        // Wait until a 'reload' button appears
        new MutationObserver((mutations, observer) => {
            if (document.querySelector('#components-reconnect-modal h5 a')) {
                // Now every 10 seconds, see if the server appears to be back, and if so, reload
                async function attemptReload() {
                    await fetch(''); // Check the server really is back
                    location.reload();
                }
                observer.disconnect();
                attemptReload();
                setInterval(attemptReload, 10000);
            }
        }).observe(document.body, { childList: true, subtree: true });
    </script>
</body>

</html>

<script>

    function isDevice() {

        return /android|webos|iphone|ipad|ipod|blackberry|iemobile|opera mini|mobile/i.test(navigator.userAgent);

    }

</script>

Route.razor

@using App.Components.Layout
@* <Router AppAssembly="@typeof(Program).Assembly" AdditionalAssemblies="new[] { typeof(Client._Imports).Assembly }">
    <Found Context="routeData">
        <AuthorizeRouteView RouteData="@routeData" DefaultLayout="@typeof(Layout.MainLayout)" />
        <FocusOnNavigate RouteData="@routeData" Selector="h1" />
    </Found>
</Router>
 *@

 <Router AppAssembly="@typeof(Program).Assembly">
    <Found Context="routeData">
        <AuthorizeRouteView RouteData="@routeData" DefaultLayout="@typeof(MainLayout)">
            <NotAuthorized>
                <App.Components.Pages.Account.Login />
            </NotAuthorized>
        </AuthorizeRouteView>
    </Found>
    <NotFound>
        <CascadingAuthenticationState>
            <LayoutView Layout="@typeof(MainLayout)">
                <p>Sorry, there's nothing at this address.</p>
            </LayoutView>
        </CascadingAuthenticationState>
    </NotFound>
</Router>

解决方案

问题根源

  1. 初始化阶段阻塞渲染:OnInitializedAsync属于组件初始化生命周期环节,Blazor Server会等待该方法完全执行完毕后才会进行第一次组件渲染。因此你在该方法内的延迟操作会导致页面完全停滞,直到方法执行结束才会输出任何内容,包括加载提示。
  2. 未更新组件状态:当前代码中既没有给SeriLogs赋值(实际数据获取被注释),也没有触发组件状态更新的逻辑,导致组件无法感知数据加载完成的状态变化。
  3. 重复加载触发:组件可能因路由跳转、框架内部渲染机制或Syncfusion Grid特性触发多次初始化,进而导致重复的数据请求。

修复步骤

1. 调整异步加载时机

改用OnAfterRenderAsync执行数据加载,该方法会在组件第一次渲染完成后触发,此时加载提示已经显示在页面上,用户能及时看到反馈。

2. 添加加载状态标志

新增isLoading变量控制加载提示的显示,同时避免重复触发数据请求。

3. 正确更新状态并触发渲染

数据加载完成后给SeriLogs赋值,并手动调用StateHasChanged()触发组件重新渲染。

修改后的Seri-Log.Razor关键代码:

@* 页面部分调整加载判断逻辑 *@
@if (Error != null)
{
    <div class="c-error">
        <h3>Error</h3>
        <h5>@Error</h5>
    </div>
}
else if (isLoading)
{
    <span>Loading Seri Logs......</span>
}
else
{
    <hr />
    <h3>End Local: @CurrenTimeLocalEnd</h3>
    <!-- 原Grid代码保留 -->
}

@code {
    // 新增加载状态标志
    private bool isLoading = true;

    protected override async Task OnAfterRenderAsync(bool firstRender)
    {
        if (firstRender)
        {
            CurrenTimeLocalStart = DateTime.UtcNow.ToLocalTime();
            try
            {
                await Task.Delay(2000);
                // 替换为实际数据获取逻辑
                SeriLogs = await maintenanceStore.ListRelated();
                CurrenTimeLocalEnd = DateTime.UtcNow.ToLocalTime();
            }
            catch (Exception ex)
            {
                Error = ex.Message;
            }
            finally
            {
                isLoading = false;
                StateHasChanged(); // 手动触发组件渲染更新
            }
        }
    }
}

额外说明

如果坚持使用OnInitializedAsync,可以在异步操作前手动调用StateHasChanged(),但这种方式在Blazor Server中无法保证加载提示优先显示,因为初始化阶段的渲染会等待异步方法完成,因此更推荐使用OnAfterRenderAsync的方案。

内容的提问来源于stack exchange,提问作者tvbRPTI2754

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.04 10:12:07