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将服务器预渲染的数据传递给客户端,避免重复请求:
- 在Razor组件中注入
PersistComponentState:
@inject PersistComponentState PersistComponentState
- 修改逻辑代码,添加状态持久化和读取逻辑:
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:调整初始状态,优化用户体验
如果不想修改预渲染配置,也可以通过调整初始值避免闪现:
- 将
IsLoading初始值设为true,确保初始渲染(包括预渲染)都显示加载状态:
public bool IsLoading { get; set; } = true;
- 将
Projects初始化为空集合,避免初始时显示空白或错误:
public IEnumerable<Project> Projects = Enumerable.Empty<Project>();
这样用户打开页面时会直接看到加载动画,不会闪现数据,加载完成后显示内容,虽然仍会执行两次加载,但体验更流畅。
内容的提问来源于stack exchange,提问作者Baldie47
相关产品推荐
相关产品推荐

