Blazor如何实时渲染未完全加载的预加载数据?
实现Blazor实时渲染已处理数据的最佳方案
针对你提到的大量数据(2000万条)需要等待全部生成后才渲染的问题,核心解决思路是分批生成/加载数据+增量更新UI,结合Blazor的组件状态自动重渲染特性,让用户逐步看到内容。以下是具体可行的方案:
方案一:异步流(IAsyncEnumerable)+ 增量更新
将服务改为返回异步流,边生成数据边传递给前端,页面逐步接收并添加到列表,实时渲染内容。
1. 修改服务方法
把返回类型从Task<WeatherForecast[]>改为IAsyncEnumerable<WeatherForecast>,通过yield return分批返回数据:
public async IAsyncEnumerable<WeatherForecast> GetForecastAsync(DateOnly startDate) { var totalItems = 20000000; for (int index = 1; index <= totalItems; index++) { // 生成单条数据 yield return new WeatherForecast { Date = startDate.AddDays(index), TemperatureC = Random.Shared.Next(-20, 55), Summary = Summaries[Random.Shared.Next(Summaries.Length)] }; // 每生成100条让出线程,给UI更新留时间(平衡实时性与性能) if (index % 100 == 0) { await Task.Yield(); } } }
2. 修改页面代码
用List<WeatherForecast>存储数据,异步遍历流并逐步添加,按需触发UI更新:
@page "/fetchdata" @using BlazorApp1.Data @inject WeatherForecastService ForecastService <PageTitle>Weather forecast</PageTitle> <h1>Weather forecast</h1> <p>This component demonstrates fetching data from a service.</p> <table class="table"> <thead> <tr> <th>Date</th> <th>Temp. (C)</th> <th>Temp. (F)</th> <th>Summary</th> </tr> </thead> <tbody> @foreach (var forecast in forecasts) { <tr> <td>@forecast.Date.ToShortDateString()</td> <td>@forecast.TemperatureC</td> <td>@forecast.TemperatureF</td> <td>@forecast.Summary</td> </tr> } </tbody> </table> @if (isLoading) { <p><em>Loading more data...</em></p> } @code { private List<WeatherForecast> forecasts = new(); private bool isLoading; protected override async Task OnInitializedAsync() { isLoading = true; await foreach (var forecast in ForecastService.GetForecastAsync(DateOnly.FromDateTime(DateTime.Now))) { forecasts.Add(forecast); // 每添加100条更新一次UI,避免频繁重渲染导致性能下降 if (forecasts.Count % 100 == 0) { StateHasChanged(); } } isLoading = false; StateHasChanged(); } }
方案二:异步流+虚拟滚动(应对超大量数据)
如果数据量达到2000万条,直接渲染所有DOM节点会导致页面严重卡顿,此时结合Blazor的Virtualize组件实现虚拟滚动,只渲染可见区域的行,兼顾实时性与性能。
页面代码示例
@page "/fetchdata" @using BlazorApp1.Data @inject WeatherForecastService ForecastService <PageTitle>Weather forecast</PageTitle> <h1>Weather forecast</h1> <p>This component demonstrates real-time data loading with virtual scrolling.</p> <Virtualize ItemsProvider="@LoadForecastData" Context="forecast"> <table class="table"> <thead> <tr> <th>Date</th> <th>Temp. (C)</th> <th>Temp. (F)</th> <th>Summary</th> </tr> </thead> <tbody> <tr> <td>@forecast.Date.ToShortDateString()</td> <td>@forecast.TemperatureC</td> <td>@forecast.TemperatureF</td> <td>@forecast.Summary</td> </tr> </tbody> </table> <Placeholder> <p><em>Loading data...</em></p> </Placeholder> </Virtualize> @code { private List<WeatherForecast> loadedForecasts = new(); private const int TotalItems = 20000000; private bool isFullyLoaded; private async ValueTask<ItemsProviderResult<WeatherForecast>> LoadForecastData(ItemsProviderRequest request) { if (isFullyLoaded) { var paginatedItems = loadedForecasts.Skip(request.StartIndex).Take(request.Count); return new ItemsProviderResult<WeatherForecast>(paginatedItems, TotalItems); } // 每次加载100条数据 var itemsToLoad = Math.Min(request.Count, TotalItems - loadedForecasts.Count); for (int i = 0; i < itemsToLoad; i++) { var itemIndex = loadedForecasts.Count + 1; loadedForecasts.Add(new WeatherForecast { Date = DateOnly.FromDateTime(DateTime.Now).AddDays(itemIndex), TemperatureC = Random.Shared.Next(-20, 55), Summary = WeatherForecastService.Summaries[Random.Shared.Next(WeatherForecastService.Summaries.Length)] }); await Task.Delay(1); // 模拟异步生成耗时 } isFullyLoaded = loadedForecasts.Count >= TotalItems; var resultItems = loadedForecasts.Skip(request.StartIndex).Take(request.Count); return new ItemsProviderResult<WeatherForecast>(resultItems, TotalItems); } }
关键注意事项
- 控制UI更新频率:避免每添加一条数据就更新UI,建议批量(如100条)更新一次,减少重渲染开销。
- 虚拟滚动的必要性:超大量数据下必须使用虚拟滚动,否则DOM节点过多会导致页面响应缓慢甚至崩溃。
- 服务端适配:如果实际场景是从数据库获取数据,可将服务改为分页查询,配合前端增量加载,逻辑与异步流一致。
内容的提问来源于stack exchange,提问作者SolarOsQuiere
相关产品推荐
相关产品推荐

