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

Blazor Server中Radzen DataGrid重复加载问题求助

Radzen DataGrid在Blazor Server中重复加载/数据闪现问题

问题现象

首次打开页面时,数据先闪现半秒(已有数据显示),随后进入加载状态,约2秒后再次显示网格内容。移除IsLoading属性后,表现为数据闪现半秒后空白约2秒再显示,无加载动画。所有数据加载逻辑已放在OnInitializedAsync中,并按照Radzen官方示例使用IsLoading控制加载状态。

相关代码

Razor代码段

@page "/projectlist"
@page "/"

@inject ISqlData _db
@inject AuthenticationStateProvider AuthenticationStateProvider
@inject NavigationManager navigationManager

@if (IsDetail == true)
{
    <ProjectDetail OnDetailShown="UpdateDetailView" CalendarPeriod="CalendarPeriod" Project="Project"></ProjectDetail>
}
else
{
    <h3><p class="text-center ">Project List</p></h3>

    <RadzenPanel Style="width: calc(100vw - 80px)">
        <RadzenDataGrid style="height: calc(100vh - 175px)" 
                        AllowPaging="true" 
                        AllowColumnResize="true" 
                        PageSize="20"  
                        IsLoading="IsLoading" 
                        AllowSorting="true" 
                        ShowPagingSummary="true" 
                        AllowColumnReorder="true"  
                        AllowMultiColumnSorting="true"  
                        AllowFiltering="true" 
                        FilterMode="FilterMode.Simple" 
                        FilterCaseSensitivity="FilterCaseSensitivity.CaseInsensitive" 
                        Data="@Projects" 
                        TItem="Project" >
            <Columns>
                <RadzenDataGridColumn TItem="Project" 
                                      Property="ProjectNumber" 
                                      Title="Project Number" 
                                      Sortable="false" 
                                      FilterOperator="FilterOperator.Contains"  
                                      Width="130px" 
                                      Pickable="false" 
                                      Frozen="true"  >
                    <Template Context="data">
                        <RadzenButton Click=@(args => OnClick(data.ProjectId, data.ProjectStatus)) 
                                      Shade="Shade.Dark" 
                                      Text="@data.ProjectNumber" 
                                      Size="ButtonSize.Small" 
                                      ButtonStyle="ButtonStyle.Success" />
                    </Template>
                </RadzenDataGridColumn>
                <RadzenDataGridColumn TItem="Project" Property="Name" Title="Project Name" MinWidth="300px"  />
                <RadzenDataGridColumn TItem="Project" Property="ContractType" Title="Contract Type"  MinWidth="300px" />
                <RadzenDataGridColumn TItem="Project" Property="PtdUnbilled" Title="Beginning WIP Balance" FormatString="{0:0,0.00}"  TextAlign="TextAlign.Right" MinWidth="210px"   Width="210px"  />
            </Columns>
        </RadzenDataGrid>
    </RadzenPanel>
}

逻辑代码段

public IEnumerable<Project> Projects;
private Employee Employee { get; set; } = null!;
private string PersonnelNo { get; set; } = null!;
public string EmployeeAdName { get; set; }
public CalendarPeriod CalendarPeriod { get; set; } = null!;
public IEnumerable<ProjectWip> ProjectWipCalculations { get; set; }
public bool IsDetail { get; set; }
public Project Project { get; set; }
public bool IsLoading { get; set; }


protected override async Task  OnParametersSetAsync()
{

}


protected override async Task OnInitializedAsync()
{
    IsLoading = true;

    EmployeeAdName = (await AuthenticationStateProvider.GetAuthenticationStateAsync()).User.Identity!.Name!;
    CalendarPeriod = await _db.GetCalendarPeriodAsync();
    
    Employee = await _db.GetEmployeeDataAsync(EmployeeAdName);

    PersonnelNo = Employee.PersonnelNumber;

    Projects = await _db.GetProjectsAsync(PersonnelNo);

    var enumerable = Projects.ToList();
    var projectList = enumerable.Select(x => x.ProjectId).ToArray();

    ProjectWipCalculations = await _db.GetCurrentMonthWIPData(projectList, CalendarPeriod.PeriodFrom);

    foreach (var project in enumerable)
    {
        var projectWip = ProjectWipCalculations.FirstOrDefault(p => p.ProjectId == project.ProjectId);
        if (projectWip != null)
        {
            project.CurrMonthInvoiceTotal = projectWip.CurrMonthInvoiceTotal;
        }
    }
    IsLoading = false;
}

private void OnClick(int projectId, string projectStatus)
{
    IsDetail = true;
    Project = Projects.First(x => x.ProjectId == projectId);
    Project.ProjectStatus = projectStatus;
}

private void UpdateDetailView()
{
    IsDetail = false;
}

问题根源

这个现象的核心原因是Blazor Server的预渲染机制:

  • 服务器会先执行一次组件的生命周期方法(包括OnInitializedAsync)进行预渲染,生成带有数据的页面返回给客户端;
  • 客户端建立SignalR连接后,组件会重新初始化,再次执行OnInitializedAsync加载数据;
  • 初始时IsLoading默认值为false,预渲染的页面会直接显示数据,客户端重新加载时IsLoading被设为true,因此出现“数据闪现→加载状态→再次显示数据”的流程。

解决方案

方案1:禁用预渲染(最简单直接)

修改Program.cs中的渲染模式配置,关闭预渲染:

builder.Services.AddRazorComponents()
    .AddInteractiveServerRenderMode(prerender: false);

这样组件只会在客户端初始化一次,不会出现两次加载的情况,直接解决闪现问题。

方案2:保留预渲染,避免重复加载

如果需要保留预渲染的优势(SEO、首屏速度),可以通过PersistComponentState将服务器预渲染的数据传递给客户端,避免重复请求:

  1. 在Razor组件中注入PersistComponentState:
@inject PersistComponentState PersistComponentState
  1. 修改逻辑代码,添加状态持久化和读取逻辑:
public IEnumerable<Project> Projects = Enumerable.Empty<Project>();
public bool IsLoading { get; set; } = true;
private bool _hasLoadedState;

protected override async Task OnInitializedAsync()
{
    // 读取预渲染时保存的状态,避免重复加载
    if (PersistComponentState.TryGetPersistedState<IEnumerable<Project>>("Projects", out var savedProjects) &&
        PersistComponentState.TryGetPersistedState<CalendarPeriod>("CalendarPeriod", out var savedPeriod))
    {
        Projects = savedProjects;
        CalendarPeriod = savedPeriod;
        IsLoading = false;
        _hasLoadedState = true;
        return;
    }

    // 原加载逻辑不变
    EmployeeAdName = (await AuthenticationStateProvider.GetAuthenticationStateAsync()).User.Identity!.Name!;
    CalendarPeriod = await _db.GetCalendarPeriodAsync();
    
    Employee = await _db.GetEmployeeDataAsync(EmployeeAdName);
    PersonnelNo = Employee.PersonnelNumber;
    Projects = await _db.GetProjectsAsync(PersonnelNo);

    var enumerable = Projects.ToList();
    var projectList = enumerable.Select(x => x.ProjectId).ToArray();
    ProjectWipCalculations = await _db.GetCurrentMonthWIPData(projectList, CalendarPeriod.PeriodFrom);

    foreach (var project in enumerable)
    {
        var projectWip = ProjectWipCalculations.FirstOrDefault(p => p.ProjectId == project.ProjectId);
        if (projectWip != null)
        {
            project.CurrMonthInvoiceTotal = projectWip.CurrMonthInvoiceTotal;
        }
    }

    // 保存状态,供客户端初始化时读取
    if (!_hasLoadedState)
    {
        PersistComponentState.PersistAsJson("Projects", Projects);
        PersistComponentState.PersistAsJson("CalendarPeriod", CalendarPeriod);
    }

    IsLoading = false;
}

方案3:调整初始状态,优化用户体验

如果不想修改预渲染配置,也可以通过调整初始值避免闪现:

  1. 将IsLoading初始值设为true,确保初始渲染(包括预渲染)都显示加载状态:
public bool IsLoading { get; set; } = true;
  1. 将Projects初始化为空集合,避免初始时显示空白或错误:
public IEnumerable<Project> Projects = Enumerable.Empty<Project>();

这样用户打开页面时会直接看到加载动画,不会闪现数据,加载完成后显示内容,虽然仍会执行两次加载,但体验更流畅。


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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.14 19:05:15